1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3use alloc::format;
4use alloc::string::{String, ToString};
5use alloc::vec;
6use alloc::vec::Vec;
7use arrayvec::ArrayVec;
8use core::fmt::Write;
9
10use naga::{
11 Expression, Handle, Module, ShaderStage, TypeInner, back,
12 proc::{self, NameKey},
13 valid::ModuleInfo,
14};
15
16use crate::conv::{self, BinOpClassified};
17use crate::ra::{self, PrintAst as _};
18use crate::util::Gensym;
19use crate::{Config, Error};
20use crate::{config::WriterFlags, util::GlobalKind};
21
22type BackendResult = Result<(), Error>;
26
27#[derive(Clone, Copy, Debug)]
43enum Indirection {
44 Place,
47
48 Ordinary,
52
53 Ref,
56}
57
58#[derive(Clone, Copy, Debug)]
69pub(crate) enum TypeTranslation {
70 RustScalar,
72
73 ShaderScalar,
78
79 Simd,
83}
84impl From<naga::AddressSpace> for TypeTranslation {
85 fn from(value: naga::AddressSpace) -> Self {
86 match value {
87 naga::AddressSpace::Function | naga::AddressSpace::Private => Self::Simd,
89
90 naga::AddressSpace::Uniform
93 | naga::AddressSpace::Handle
94 | naga::AddressSpace::WorkGroup
95 | naga::AddressSpace::Immediate
96 | naga::AddressSpace::Storage { .. } => Self::ShaderScalar,
97 naga::AddressSpace::TaskPayload
98 | naga::AddressSpace::RayPayload
99 | naga::AddressSpace::IncomingRayPayload => {
100 unimplemented!("mesh and raytracing shaders are not supported")
101 }
102 }
103 }
104}
105
106const FN_INTERNAL_TYPES_PREFIX: &str = "v_";
109
110#[allow(missing_debug_implementations, reason = "TODO")]
118pub struct Writer {
119 config: Config,
120 names: naga::FastHashMap<NameKey, String>,
121 namer: proc::Namer,
122 named_expressions: naga::FastIndexMap<Handle<Expression>, String>,
123}
124
125enum ExpressionCtx<'a> {
126 Global {
127 module: &'a Module,
128 module_info: &'a ModuleInfo,
129 expressions: &'a naga::Arena<Expression>,
130 },
131 Function {
132 module: &'a Module,
133 func_ctx: &'a back::FunctionCtx<'a>,
135 },
136}
137
138impl<'a> ExpressionCtx<'a> {
139 #[track_caller]
140 fn expect_func_ctx(&self) -> &'a back::FunctionCtx<'a> {
141 match self {
142 ExpressionCtx::Function { func_ctx, .. } => func_ctx,
143 ExpressionCtx::Global { .. } => {
144 unreachable!("attempting to access the function context outside of a function")
145 }
146 }
147 }
148
149 fn module(&self) -> &'a Module {
150 match self {
151 ExpressionCtx::Global { module, .. } => module,
152 ExpressionCtx::Function { module, .. } => module,
153 }
154 }
155
156 fn expressions(&self) -> &'a naga::Arena<Expression> {
157 match self {
158 ExpressionCtx::Global { expressions, .. } => expressions,
159 ExpressionCtx::Function { func_ctx, .. } => func_ctx.expressions,
160 }
161 }
162
163 fn types(&self) -> &'a naga::UniqueArena<naga::Type> {
164 &self.module().types
165 }
166
167 fn resolve_type(&self, handle: Handle<Expression>) -> &'a TypeInner {
168 match self {
169 ExpressionCtx::Global { module_info, .. } => &module_info[handle],
170 ExpressionCtx::Function { func_ctx, .. } => &func_ctx.info[handle].ty,
171 }
172 .inner_with(self.types())
173 }
174}
175
176impl Writer {
177 #[must_use]
179 pub fn new(config: Config) -> Self {
180 Writer {
181 config,
182 names: naga::FastHashMap::default(),
183 namer: proc::Namer::default(),
184 named_expressions: naga::FastIndexMap::default(),
185 }
186 }
187
188 fn reset(&mut self, module: &Module) {
189 let Self {
190 config,
191 names,
192 namer,
193 named_expressions,
194 } = self;
195 names.clear();
196 namer.reset(
197 module,
198 conv::keywords_2024(),
199 proc::KeywordSet::empty(),
200 proc::CaseInsensitiveKeywordSet::empty(),
201 &[FN_INTERNAL_TYPES_PREFIX],
202 &mut self.names,
203 );
204
205 if let Some(g) = &config.global_struct {
208 namer.call(g);
209 }
210 if let Some(r) = &config.resource_struct {
211 namer.call(r);
212 }
213 named_expressions.clear();
214 }
215
216 pub fn write(
225 &mut self,
226 out: &mut dyn Write,
227 module: &Module,
228 info: &ModuleInfo,
229 ) -> BackendResult {
230 let top_level_items = self.translate_module(module, info)?;
231
232 let print_ctx = ra::PrintCtx {
233 config: &self.config,
234 indent: back::Level(0),
235 };
236 for item in top_level_items {
237 item.write(out, print_ctx)?;
238 }
239
240 Ok(())
241 }
242
243 pub(crate) fn translate_module(
249 &mut self,
250 module: &Module,
251 info: &ModuleInfo,
252 ) -> Result<Vec<ra::Item>, Error> {
253 if !module.overrides.is_empty() {
254 return Err(Error::Unimplemented("pipeline constants".into()));
255 }
256
257 self.reset(module);
258
259 let visibility = self.visibility();
260 let mut top_level_items: Vec<ra::Item> = Vec::new();
261
262 for (handle, ty) in module.types.iter() {
264 if let TypeInner::Struct { ref members, .. } = ty.inner {
265 top_level_items.extend(self.translate_struct_definition(module, handle, members)?);
266 }
267 }
268
269 for (handle, _) in module.constants.iter().filter(|&(_, c)| c.name.is_some()) {
271 top_level_items.push(ra::Item::Const(
272 self.translate_global_constant(module, info, handle)?,
273 ));
274 }
275
276 let any_resource_requires_lifetime = GlobalKind::Resource
278 .filter(&module.global_variables)
279 .any(|(_, global)| matches!(module.types[global.ty].inner, TypeInner::Image { .. }));
280 let resource_lifetime_generics = if any_resource_requires_lifetime {
281 ra::Generics::LtG
282 } else {
283 ra::Generics::None
284 };
285 {
286 let mut resource_iter = GlobalKind::Resource.filter(&module.global_variables);
287 if let Some(ref resource_struct_name) = self.config.resource_struct {
288 let resource_struct_ast = ra::StructItem {
289 attributes: vec![],
290 visibility: ra::Visibility::Private, name: resource_struct_name.clone(),
292 generics: resource_lifetime_generics,
293 fields: resource_iter
294 .map(|(handle, global)| {
295 self.translate_global_variable_to_struct_field(module, global, handle)
296 })
297 .collect::<Result<Vec<_>, _>>()?,
298 };
299
300 top_level_items.push(ra::Item::Struct(resource_struct_ast));
301 } else if let Some((_, example)) = resource_iter.next() {
302 return Err(Error::ResourcesNotEnabled {
303 example: example.name.clone().unwrap_or_default(),
304 });
305 }
306 }
307
308 let ref_to_resources_ty = self.config.resource_struct.as_ref().map(|name| {
309 ra::Type::Ptr(
310 ra::PtrKind::Shared(Some("g")),
311 Box::new(ra::Type::User(name.clone(), resource_lifetime_generics)),
312 )
313 });
314
315 let global_lifetime_generics =
317 if self.config.global_struct.is_some() && self.config.resource_struct.is_some() {
318 ra::Generics::LtG
319 } else {
320 resource_lifetime_generics
323 };
324 {
325 let mut global_variable_iter = GlobalKind::Variable.filter(&module.global_variables);
326 if let Some(ref global_struct_name) = self.config.global_struct {
327 let mut global_struct_fields = Vec::new();
328 if let Some(ref ref_to_resources_ty) = ref_to_resources_ty {
329 global_struct_fields.push(ra::Field {
330 attributes: vec![],
331 visibility,
332 name: "resources".into(),
333 ty: ref_to_resources_ty.clone(),
334 });
335 }
336 for (handle, global) in global_variable_iter {
337 global_struct_fields.push(
338 self.translate_global_variable_to_struct_field(module, global, handle)?,
339 );
340 }
341
342 let global_struct_item = ra::StructItem {
343 attributes: vec![],
344 visibility: ra::Visibility::Private, name: global_struct_name.clone(),
346 generics: global_lifetime_generics,
347 fields: global_struct_fields,
348 };
349 top_level_items.push(ra::Item::Struct(global_struct_item));
350
351 let global_struct_constructor_fn = ra::FunctionItem {
352 attributes: vec![],
353 visibility,
354 const_: true,
355 name: "new".into(),
356 parameters: if let Some(ref_to_resources_ty) = ref_to_resources_ty {
359 vec![(
360 ra::Pattern::Binding("resources".into()),
361 ref_to_resources_ty,
362 )]
363 } else {
364 vec![]
365 },
366 self_param: None,
367 return_type: ra::Type::Self_,
368 body: ra::Block::expr(ra::Expr::Struct(ra::Type::Self_, {
369 let mut fields = Vec::new();
370 if self.config.resource_struct.is_some() {
371 fields.push(("resources".into(), ra::Expr::Ident("resources".into())));
373 }
374 for (handle, global) in
375 GlobalKind::Variable.filter(&module.global_variables)
376 {
377 fields.push((
378 self.names[&NameKey::GlobalVariable(handle)].clone(),
379 self.global_variable_as_field_initializer_expr(
380 module, info, global,
381 )?,
382 ));
383 }
384 fields
385 })),
386 };
387
388 top_level_items.push(ra::Item::Impl(
389 global_lifetime_generics,
390 None,
391 ra::Type::User(global_struct_name.clone(), global_lifetime_generics),
392 vec![ra::Item::Function(global_struct_constructor_fn)],
393 ));
394
395 if self.config.resource_struct.is_none() {
396 top_level_items.push(ra::Item::Impl(
399 global_lifetime_generics,
400 Some(ra::Trait::Default),
401 ra::Type::User(global_struct_name.clone(), global_lifetime_generics),
402 vec![ra::Item::Function(ra::FunctionItem {
403 attributes: Vec::new(),
404 visibility: ra::Visibility::Private, const_: false,
406 name: "default".into(),
407 self_param: None,
408 parameters: vec![],
409 return_type: ra::Type::Self_,
410 body: ra::Block::expr(ra::Expr::Call(
411 Box::new(ra::Expr::QualifiedPath(ra::Type::Self_, "new")),
412 vec![],
413 )),
414 })],
415 ));
416 }
417 } else if let Some((_, example)) = global_variable_iter.next() {
418 return Err(Error::GlobalVariablesNotEnabled {
419 example: example.name.clone().unwrap_or_default(),
420 });
421 }
422 }
423
424 let mut maybe_impl_items: Vec<ra::Item> = Vec::new();
426
427 for (handle, function) in module.functions.iter() {
429 let fun_info = &info[handle];
430
431 let func_ctx = back::FunctionCtx {
432 ty: back::FunctionType::Function(handle),
433 info: fun_info,
434 expressions: &function.expressions,
435 named_expressions: &function.named_expressions,
436 };
437
438 maybe_impl_items.extend(self.translate_function(
440 module,
441 function,
442 &func_ctx,
443 vec![],
444 )?);
445 }
446
447 for (index, ep) in module.entry_points.iter().enumerate() {
449 let entry_point_attributes = match ep.stage {
450 ShaderStage::Vertex
451 | ShaderStage::Fragment
452 | ShaderStage::Task
453 | ShaderStage::Mesh
454 | ShaderStage::RayGeneration
455 | ShaderStage::Miss
456 | ShaderStage::AnyHit
457 | ShaderStage::ClosestHit => vec![ra::Attribute::Stage(ep.stage)],
458 ShaderStage::Compute => vec![
459 ra::Attribute::Stage(ShaderStage::Compute),
460 ra::Attribute::WorkGroupSize(ep.workgroup_size),
461 ],
462 };
463
464 let func_ctx = back::FunctionCtx {
465 ty: back::FunctionType::EntryPoint(index.try_into().unwrap()),
466 info: info.get_entry_point(index),
467 expressions: &ep.function.expressions,
468 named_expressions: &ep.function.named_expressions,
469 };
470 maybe_impl_items.extend(self.translate_function(
471 module,
472 &ep.function,
473 &func_ctx,
474 entry_point_attributes,
475 )?);
476 }
477
478 if let Some(name) = self.config.impl_type() {
480 top_level_items.push(ra::Item::Impl(
481 global_lifetime_generics,
482 None,
483 ra::Type::User(name.to_string(), global_lifetime_generics),
484 maybe_impl_items,
485 ))
486 } else {
487 top_level_items.extend(maybe_impl_items);
488 }
489
490 Ok(top_level_items)
491 }
492
493 fn translate_function(
497 &mut self,
498 module: &Module,
499 func: &naga::Function,
500 func_ctx: &back::FunctionCtx<'_>,
501 extra_attributes: Vec<ra::Attribute>,
502 ) -> Result<[ra::Item; 2], Error> {
503 Ok([
504 self.translate_function_inner(module, func, func_ctx, true, extra_attributes)?,
505 self.translate_function_inner(module, func, func_ctx, false, vec![])?,
506 ])
507 }
508
509 fn translate_function_inner(
510 &mut self,
511 module: &Module,
512 func: &naga::Function,
513 func_ctx: &back::FunctionCtx<'_>,
514 is_public_shim: bool,
515 mut attributes: Vec<ra::Attribute>,
516 ) -> Result<ra::Item, Error> {
517 let signature_type_translation = if is_public_shim {
518 TypeTranslation::RustScalar
519 } else {
520 TypeTranslation::Simd
521 };
522
523 if !is_public_shim {
524 attributes.push(ra::Attribute::AllowFunctionBody);
526 }
527
528 let shader_func_name = match func_ctx.ty {
529 back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
530 back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
531 };
532 let name_prefix = if is_public_shim {
533 ""
534 } else {
535 FN_INTERNAL_TYPES_PREFIX
536 };
537 let rust_func_name = format!("{name_prefix}{shader_func_name}");
538 let visibility = if is_public_shim {
539 self.visibility()
540 } else {
541 ra::Visibility::Private
542 };
543
544 let self_param = if self.config.functions_are_methods() {
545 Some(ra::PtrKind::Shared(None))
547 } else if func_ctx.info.global_variable_count() > 0 {
548 unreachable!(
549 "function has globals but globals are not enabled; \
550 should have been rejected earlier"
551 )
552 } else {
553 None
554 };
555
556 let use_into_for_arg = |arg: &naga::FunctionArgument| {
557 matches!(
558 module.types[arg.ty].inner,
559 TypeInner::Scalar { .. } | TypeInner::Vector { .. }
560 )
561 };
562
563 let mut rust_params: Vec<(ra::Pattern, ra::Type)> =
565 Vec::with_capacity(func.arguments.len());
566 for (index, arg) in func.arguments.iter().enumerate() {
567 let argument_name = &self.names[&func_ctx.argument_key(index.try_into().unwrap())];
568
569 let rust_type = if is_public_shim && use_into_for_arg(arg) {
572 ra::Type::ImplInto(Box::new(self.type_ast(
574 module,
575 arg.ty,
576 TypeTranslation::ShaderScalar,
577 )?))
578 } else {
579 self.type_ast(module, arg.ty, signature_type_translation)?
580 };
581
582 rust_params.push((ra::Pattern::Binding(argument_name.clone()), rust_type));
583 }
584
585 let return_type = if let Some(ref result) = func.result {
586 self.type_ast(module, result.ty, signature_type_translation)?
590 } else {
591 ra::Type::Unit
592 };
593
594 let body = if is_public_shim {
595 let mut call_args = Vec::new();
598 for (index, arg) in func.arguments.iter().enumerate() {
599 let argument_name = &self.names[&func_ctx.argument_key(index.try_into().unwrap())];
600 let argument_expr = if use_into_for_arg(arg) {
601 ra::Expr::call_rt(ra::RtItem::IntoFn, [ra::Expr::Ident(argument_name.clone())])
602 } else {
603 ra::Expr::Ident(argument_name.clone())
604 };
605 call_args.push(argument_expr);
606 }
607
608 ra::Block::expr(ra::Expr::call_rt(
612 ra::RtItem::IntoFn,
613 [ra::Expr::call_maybe_self(
614 self.config.functions_are_methods(),
615 format!("{FN_INTERNAL_TYPES_PREFIX}{shader_func_name}"),
616 call_args,
617 )],
618 ))
619 } else {
620 let mut body_statements = Vec::new();
621
622 for (handle, local) in func.local_variables.iter() {
624 let local_name = self.names[&func_ctx.name_key(handle)].clone();
625 let init_expression = if let Some(init) = local.init {
626 Some(self.translate_expr(
627 init,
628 &ExpressionCtx::Function {
629 module,
630 func_ctx,
631 },
633 )?)
634 } else {
635 None
636 };
637
638 body_statements.push(ra::Statement::Let(
639 ra::Pattern::BindingMut(local_name),
640 Some(self.type_ast(module, local.ty, TypeTranslation::Simd)?),
641 init_expression,
642 ));
643 }
644 if !func.local_variables.is_empty() {
645 body_statements.push(ra::Statement::BlankLine);
646 }
647
648 for sta in func.body.iter() {
650 body_statements.extend(self.translate_statement(module, sta, func_ctx)?);
651 }
652
653 self.named_expressions.clear();
654
655 ra::Block(body_statements, None)
656 };
657
658 Ok(ra::Item::Function(ra::FunctionItem {
659 attributes,
660 visibility,
661 const_: false,
662 name: rust_func_name,
663 self_param,
664 parameters: rust_params,
665 return_type,
666 body,
667 }))
668 }
669
670 fn translate_struct_definition(
678 &self,
679 module: &Module,
680 struct_handle: Handle<naga::Type>,
681 members: &[naga::StructMember],
682 ) -> Result<Vec<ra::Item>, Error> {
683 let visibility = self.visibility();
685 let name = &self.names[&NameKey::Type(struct_handle)];
686 let struct_attributes = vec![ra::Attribute::DeriveStructTraits, ra::Attribute::ReprC];
687 let self_ty = ra::Type::User(name.clone(), ra::Generics::None);
688
689 let mut fields: Vec<ra::Field> = Vec::with_capacity(members.len());
690 let mut dyn_sized = false;
691 for (member_name, member) in self.iter_struct_members(struct_handle, members) {
692 fields.push(ra::Field {
698 attributes: vec![],
699 visibility,
700 name: member_name.to_string(),
701 ty: self.type_ast(module, member.ty, TypeTranslation::RustScalar)?,
702 });
703
704 if module.types[member.ty]
705 .inner
706 .is_dynamically_sized(&module.types)
707 {
708 dyn_sized = true;
709 }
710 }
711
712 let mut items = vec![ra::Item::Struct(ra::StructItem {
713 attributes: struct_attributes,
714 visibility,
715 name: name.clone(),
716 generics: ra::Generics::None,
717 fields,
718 })];
719
720 if !dyn_sized {
722 let mut constructor_parameters = Vec::new();
723 let mut constructor_fields = Vec::new();
724 for (member_name, member) in self.iter_struct_members(struct_handle, members) {
725 constructor_parameters.push((
726 ra::Pattern::Binding(member_name.to_string()),
727 ra::Type::ImplInto(Box::new(self.type_ast(
728 module,
729 member.ty,
730 TypeTranslation::RustScalar,
731 )?)),
732 ));
733 constructor_fields.push((
734 member_name.to_string(),
735 ra::Expr::call_rt(
736 ra::RtItem::IntoFn,
737 [ra::Expr::Ident(member_name.to_string())],
738 ),
739 ));
740 }
741
742 let constructor_fn = ra::FunctionItem {
743 attributes: vec![],
744 visibility,
745 const_: false,
746 name: "new".into(),
747 self_param: None,
748 parameters: constructor_parameters,
749 return_type: ra::Type::Self_,
750 body: ra::Block::expr(ra::Expr::Struct(ra::Type::Self_, constructor_fields)),
751 };
752
753 items.push(ra::Item::Impl(
754 ra::Generics::None,
755 None,
756 self_ty,
757 vec![ra::Item::Function(constructor_fn)],
758 ));
759 }
760
761 Ok(items)
762 }
763
764 fn iter_struct_members<'mem, 'name>(
765 &'name self,
766 struct_handle: Handle<naga::Type>,
767 members: &'mem [naga::StructMember],
768 ) -> impl Iterator<Item = (&'name str, &'mem naga::StructMember)> {
769 members.iter().enumerate().map(move |(index, member)| {
770 let name =
771 &*self.names[&NameKey::StructMember(struct_handle, index.try_into().unwrap())];
772 (name, member)
773 })
774 }
775
776 fn translate_block(
777 &mut self,
778 module: &Module,
779 stmts: &[naga::Statement],
780 func_ctx: &back::FunctionCtx<'_>,
781 ) -> Result<ra::Block, Error> {
782 Ok(ra::Block(
783 self.translate_statements(module, stmts, func_ctx)?,
784 None,
785 ))
786 }
787
788 fn translate_statements(
789 &mut self,
790 module: &Module,
791 stmts: &[naga::Statement],
792 func_ctx: &back::FunctionCtx<'_>,
793 ) -> Result<Vec<ra::Statement>, Error> {
794 let mut output: Vec<ra::Statement> = Vec::with_capacity(stmts.len());
795 for s in stmts {
796 output.append(&mut self.translate_statement(module, s, func_ctx)?);
797 }
798 Ok(output)
799 }
800
801 fn translate_statement(
802 &mut self,
803 module: &Module,
804 stmt: &naga::Statement,
805 func_ctx: &back::FunctionCtx<'_>,
806 ) -> Result<Vec<ra::Statement>, Error> {
807 use naga::{Expression, Statement};
808
809 let expr_ctx = &ExpressionCtx::Function {
810 module,
811 func_ctx,
812 };
814
815 Ok(match *stmt {
816 Statement::Emit(ref range) => {
817 let mut output = Vec::new();
818 for handle in range.clone() {
819 let expr_info = &func_ctx.info[handle];
820 let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
823 Some(self.namer.call(name))
824 } else {
825 let expr = &func_ctx.expressions[handle];
826 let min_ref_count = expr.bake_ref_count();
827 let required_baking_expr = matches!(
829 *expr,
830 Expression::ImageLoad { .. }
831 | Expression::ImageQuery { .. }
832 | Expression::ImageSample { .. }
833 );
834 if min_ref_count <= expr_info.ref_count || required_baking_expr {
835 Some(Gensym(handle).to_string())
836 } else {
837 None
838 }
839 };
840
841 if let Some(name) = expr_name {
842 output.push(self.let_ast(
843 module,
844 handle,
845 func_ctx,
846 name.clone(),
847 self.translate_expr(handle, expr_ctx)?,
848 )?);
849 self.named_expressions.insert(handle, name);
850 }
851 }
852 output
853 }
854 Statement::If {
855 condition,
856 ref accept,
857 ref reject,
858 } => {
859 vec![ra::Statement::If(
860 Box::new(ra::Expr::call_rt(
861 ra::RtItem::ScalarIntoBranchCondition,
862 [self.translate_expr(condition, expr_ctx)?],
863 )),
864 self.translate_block(module, accept, func_ctx)?,
865 self.translate_block(module, reject, func_ctx)?,
866 )]
867 }
868 Statement::Return { value } => {
869 vec![ra::Statement::Return(
870 value
871 .map(|v| self.translate_expr(v, expr_ctx))
872 .transpose()?,
873 )]
874 }
875 Statement::Kill => vec![ra::Statement::Expr(ra::Expr::call_rt(
876 ra::RtItem::DiscardFn,
877 [],
878 ))],
879 Statement::Store { pointer, value } => {
880 vec![self.translate_store_statement(expr_ctx, pointer, value)?]
881 }
882 Statement::Call {
883 function,
884 ref arguments,
885 result,
886 } => {
887 let func_name = &self.names[&NameKey::Function(function)];
888 let rust_func_name = format!("{FN_INTERNAL_TYPES_PREFIX}{func_name}");
889 let rust_args = self.translate_exprs(expr_ctx, arguments.iter().copied())?;
890
891 let translated_call_expr = ra::Expr::call_maybe_self(
892 self.config.functions_are_methods(),
893 Cow::Owned(rust_func_name),
894 rust_args,
895 );
896
897 if let Some(result_expr) = result {
899 let name = Gensym(result_expr).to_string();
900 self.named_expressions.insert(result_expr, name.clone());
901 vec![self.let_ast(module, result_expr, func_ctx, name, translated_call_expr)?]
902 } else {
903 vec![ra::Statement::Expr(translated_call_expr)]
904 }
905 }
906 Statement::Atomic { .. } => vec![self.unimplemented_stmt("atomic operations")?],
907 Statement::ImageAtomic { .. } => {
908 vec![self.unimplemented_stmt("atomic texture operations")?]
909 }
910 Statement::WorkGroupUniformLoad { .. } => {
911 todo!("Statement::WorkGroupUniformLoad");
912 }
913 Statement::ImageStore { .. } => vec![self.unimplemented_stmt("textureStore")?],
914 Statement::Block(ref block) => {
915 vec![ra::Statement::Block(
916 None, self.translate_block(module, block, func_ctx)?,
918 )]
919 }
920 Statement::Switch {
921 selector,
922 ref cases,
923 } => {
924 let mut arms: Vec<ra::Arm> = Vec::with_capacity(cases.len());
926 let mut previous_case_was_fall_through = false;
927 for case in cases {
928 let &naga::SwitchCase {
929 value,
930 ref body,
931 fall_through,
932 } = case;
933 if fall_through && !body.is_empty() {
934 return Ok(vec![self.unimplemented_stmt(
936 "switch case with statements and fall-through",
937 )?]);
938 }
939
940 let pattern = match value {
941 naga::SwitchValue::I32(value) => ra::Pattern::LitI32(value),
942 naga::SwitchValue::U32(value) => ra::Pattern::LitU32(value),
943 naga::SwitchValue::Default => ra::Pattern::Wildcard,
944 };
945
946 let translated_body = self.translate_block(module, body, func_ctx)?;
947
948 if previous_case_was_fall_through {
949 let existing_arm = arms.last_mut().unwrap();
950 existing_arm.pattern_alternatives.push(pattern);
951 existing_arm.body.0.extend(translated_body.0);
952 } else {
953 arms.push(ra::Arm {
954 pattern_alternatives: vec![pattern],
955 body: translated_body,
956 });
957 }
958
959 previous_case_was_fall_through = case.fall_through;
960 }
961
962 vec![ra::Statement::Match(
963 Box::new(ra::Expr::call_rt(
964 ra::RtItem::ScalarIntoInner,
965 [self.translate_expr(selector, expr_ctx)?],
966 )),
967 arms,
968 )]
969 }
970 Statement::Loop {
971 ref body,
972 ref continuing,
973 break_if,
974 } => {
975 let mut rust_loop_body = Vec::with_capacity(3);
976
977 rust_loop_body.push(ra::Statement::Block(
978 Some("naga_continue"),
979 self.translate_block(module, body, func_ctx)?,
980 ));
981
982 rust_loop_body
985 .append(&mut self.translate_statements(module, continuing, func_ctx)?);
986
987 if let Some(break_if) = break_if {
988 rust_loop_body.push(ra::Statement::If(
989 Box::new(ra::Expr::call_rt(
990 ra::RtItem::ScalarIntoBranchCondition,
991 [self.translate_expr(break_if, expr_ctx)?],
992 )),
993 ra::Block(vec![ra::Statement::Break(Some("naga_break"))], None),
994 ra::Block(vec![], None),
995 ));
996 }
997
998 vec![ra::Statement::Loop(
999 "naga_break",
1000 ra::Block(rust_loop_body, None),
1001 )]
1002 }
1003 Statement::Break => vec![ra::Statement::Break(Some("naga_break"))],
1004 Statement::Continue => vec![ra::Statement::Break(Some("naga_continue"))],
1005
1006 Statement::ControlBarrier(_) | Statement::MemoryBarrier(_) => {
1007 vec![self.unimplemented_stmt("barriers")?]
1008 }
1009 Statement::RayQuery { .. } | Statement::RayPipelineFunction(_) => {
1010 vec![self.unimplemented_stmt("raytracing")?]
1011 }
1012 Statement::SubgroupBallot { .. }
1013 | Statement::SubgroupCollectiveOperation { .. }
1014 | Statement::SubgroupGather { .. } => {
1015 vec![self.unimplemented_stmt("workgroup operations")?]
1016 }
1017 Statement::CooperativeStore { .. } => {
1018 vec![self.unimplemented_stmt("cooperative store")?]
1019 }
1020 })
1021 }
1022
1023 fn translate_store_statement(
1029 &mut self,
1030 expr_ctx: &ExpressionCtx<'_>,
1031 pointer: Handle<Expression>,
1032 value_expr: Handle<Expression>,
1033 ) -> Result<ra::Statement, Error> {
1034 let pointer_type: &TypeInner = expr_ctx.resolve_type(pointer);
1035 let pointer_base_type = pointer_type
1036 .pointer_base_type()
1037 .expect("Store statement’s pointer's type not a pointer type");
1038
1039 let pointer_expr = &expr_ctx.expressions()[pointer];
1042
1043 if let TypeInner::Atomic(_) = pointer_base_type.inner_with(expr_ctx.types()) {
1044 return self.unimplemented_stmt("atomic operations");
1049 }
1050
1051 if let Expression::AccessIndex { base, index } = *pointer_expr {
1052 let access_base_type = expr_ctx.resolve_type(base);
1053 let access_pointer_base_type = access_base_type
1054 .pointer_base_type()
1055 .expect("Store statement’s access expression's base type not a pointer type");
1056
1057 if let TypeInner::Vector { .. } = access_pointer_base_type.inner_with(expr_ctx.types())
1059 {
1060 return Ok(ra::Statement::Expr(ra::Expr::Method(
1061 Box::new(self.expr_ast_with_indirection(base, expr_ctx, Indirection::Place)?),
1062 Cow::Owned(format!("set_{}", back::COMPONENTS[index as usize])),
1063 vec![self.translate_expr(value_expr, expr_ctx)?],
1064 )));
1065 }
1066 }
1067
1068 let value_expr = match TypeTranslation::from(pointer_type.pointer_space().unwrap()) {
1075 TypeTranslation::RustScalar => ra::Expr::call_rt(
1076 ra::RtItem::ScalarIntoInner,
1077 [self.translate_expr(value_expr, expr_ctx)?],
1078 ),
1079 TypeTranslation::ShaderScalar | TypeTranslation::Simd => {
1080 self.translate_expr(value_expr, expr_ctx)?
1082 }
1083 };
1084
1085 Ok(ra::Statement::Assign(
1086 self.expr_ast_with_indirection(pointer, expr_ctx, Indirection::Place)?,
1087 value_expr,
1088 ))
1089 }
1090
1091 fn let_ast(
1093 &self,
1094 module: &Module,
1095 handle: Handle<Expression>,
1096 func_ctx: &back::FunctionCtx<'_>,
1097 name: String,
1098 expr: ra::Expr,
1099 ) -> Result<ra::Statement, Error> {
1100 let rust_ty = if self.config.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1101 Some(match func_ctx.info[handle].ty {
1102 proc::TypeResolution::Handle(ty_handle) => {
1103 self.type_ast(module, ty_handle, TypeTranslation::Simd)?
1104 }
1105 proc::TypeResolution::Value(ref inner) => {
1106 self.type_ast_inner(module, inner, TypeTranslation::Simd)?
1107 }
1108 })
1109 } else {
1110 None
1111 };
1112
1113 Ok(ra::Statement::Let(
1114 ra::Pattern::Binding(name),
1115 rust_ty,
1116 Some(expr),
1117 ))
1118 }
1119
1120 fn translate_expr(
1124 &self,
1125 expr: Handle<Expression>,
1126 expr_ctx: &ExpressionCtx<'_>,
1127 ) -> Result<ra::Expr, Error> {
1128 self.expr_ast_with_indirection(expr, expr_ctx, Indirection::Ordinary)
1129 }
1130
1131 fn translate_exprs(
1133 &self,
1134 expr_ctx: &ExpressionCtx<'_>,
1135 exprs: impl IntoIterator<Item = Handle<Expression>>,
1136 ) -> Result<Vec<ra::Expr>, Error> {
1137 exprs
1138 .into_iter()
1139 .map(|expr| self.translate_expr(expr, expr_ctx))
1140 .collect()
1141 }
1142
1143 fn expr_ast_with_indirection(
1148 &self,
1149 expr: Handle<Expression>,
1150 expr_ctx: &ExpressionCtx<'_>,
1151 requested: Indirection,
1152 ) -> Result<ra::Expr, Error> {
1153 let (plain_expr, plain): (ra::Expr, Indirection) =
1156 self.expr_ast_plain_form(expr, expr_ctx)?;
1157 Ok(match (requested, plain) {
1158 (Indirection::Ordinary, Indirection::Place)
1166 | (Indirection::Ref, Indirection::Ordinary) => {
1167 ra::Expr::Borrow(ra::PtrKind::Shared(None), Box::new(plain_expr))
1168 }
1169
1170 (Indirection::Place, Indirection::Ordinary)
1173 | (Indirection::Ordinary, Indirection::Ref) => ra::Expr::Deref(Box::new(plain_expr)),
1174
1175 (Indirection::Place, Indirection::Place)
1177 | (Indirection::Ordinary, Indirection::Ordinary)
1178 | (Indirection::Ref, Indirection::Ref) => plain_expr,
1179
1180 (Indirection::Ref, Indirection::Place) | (Indirection::Place, Indirection::Ref) => {
1181 unreachable!("multi-level ref/deref")
1182 }
1183 })
1184 }
1185
1186 fn expr_ast_plain_form(
1200 &self,
1201 expr: Handle<Expression>,
1202 expr_ctx: &ExpressionCtx<'_>,
1203 ) -> Result<(ra::Expr, Indirection), Error> {
1204 if let Some(name) = self.named_expressions.get(&expr) {
1205 return Ok((ra::Expr::Ident(name.clone()), Indirection::Ordinary));
1206 }
1207
1208 let expression = &expr_ctx.expressions()[expr];
1209 let module = expr_ctx.module();
1210
1211 let mut indirection = Indirection::Ordinary;
1216
1217 let rust_expr = match *expression {
1218 Expression::Literal(literal) => {
1219 let rust_literal = match literal {
1220 naga::Literal::F16(value) => ra::Expr::LitF16(value),
1223 naga::Literal::F32(value) => ra::Expr::LitF32(value),
1224 naga::Literal::U32(value) => ra::Expr::LitU32(value),
1225 naga::Literal::I32(value) => ra::Expr::LitI32(value),
1226 naga::Literal::Bool(value) => ra::Expr::LitBool(value),
1227 naga::Literal::F64(value) => ra::Expr::LitF64(value),
1228 naga::Literal::I64(value) => ra::Expr::LitI64(value),
1229 naga::Literal::U64(value) => ra::Expr::LitU64(value),
1230 naga::Literal::AbstractInt(_) | naga::Literal::AbstractFloat(_) => {
1231 unreachable!(
1232 "abstract types should not appear in IR presented to backends"
1233 );
1234 }
1235 };
1236 ra::Expr::call_rt(ra::RtItem::Scalar, [rust_literal])
1237 }
1238 Expression::Constant(handle) => {
1239 let constant = &module.constants[handle];
1240 if constant.name.is_some() {
1241 ra::Expr::Ident(self.names[&NameKey::Constant(handle)].clone())
1242 } else {
1243 self.translate_expr(constant.init, expr_ctx)?
1244 }
1245 }
1246 Expression::ZeroValue(_ty) => {
1247 ra::Expr::call_rt(ra::RtItem::ZeroFn, [])
1249 }
1250 Expression::Compose { ty, ref components } => {
1251 self.constructor_expression_ast(ty, components, expr_ctx)?
1252 }
1253 Expression::Splat { size, value } => ra::Expr::call_rt(
1254 ra::RtItem::SplatFromScalar(size),
1255 [self.translate_expr(value, expr_ctx)?],
1256 ),
1257 Expression::Override(_) => unreachable!(),
1258 Expression::FunctionArgument(pos) => {
1259 let name_key = expr_ctx.expect_func_ctx().argument_key(pos);
1260 let name = &self.names[&name_key];
1261 ra::Expr::Ident(name.clone())
1262 }
1263 Expression::Binary { op, left, right } => match BinOpClassified::from(op) {
1264 BinOpClassified::Vectorizable(_) => ra::Expr::BinOp(
1265 Box::new(self.translate_expr(left, expr_ctx)?),
1266 op,
1267 Box::new(self.translate_expr(right, expr_ctx)?),
1268 ),
1269 BinOpClassified::ScalarBool(bop) => ra::Expr::Method(
1270 Box::new(self.translate_expr(left, expr_ctx)?),
1271 Cow::Borrowed(bop.to_vector_method()),
1272 vec![self.translate_expr(right, expr_ctx)?],
1273 ),
1274 BinOpClassified::ShortCircuit(_bop) => {
1275 ra::Expr::call_rt(
1278 ra::RtItem::Scalar,
1279 vec![ra::Expr::BinOp(
1280 Box::new(ra::Expr::TupleField(
1281 Box::new(self.translate_expr(left, expr_ctx)?),
1282 0,
1283 )),
1284 op,
1285 Box::new(ra::Expr::TupleField(
1286 Box::new(self.translate_expr(right, expr_ctx)?),
1287 0,
1288 )),
1289 )],
1290 )
1291 }
1292 },
1293 Expression::Access { base, index } => {
1294 let base_ty = expr_ctx.resolve_type(base);
1298 if let TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } = base_ty {
1299 indirection = Indirection::Place;
1300 }
1301
1302 ra::Expr::Index(
1305 Box::new(self.expr_ast_with_indirection(base, expr_ctx, indirection)?),
1306 Box::new(ra::Expr::call_rt(
1307 ra::RtItem::ScalarIntoArrayIndex,
1308 [self.translate_expr(index, expr_ctx)?],
1309 )),
1310 )
1311 }
1312 Expression::AccessIndex { base, index } => {
1313 let result_ty = expr_ctx.resolve_type(expr);
1314
1315 let base_ty_res = &expr_ctx.expect_func_ctx().info[base].ty;
1316 let mut base_ty_resolved = base_ty_res.inner_with(&module.types);
1317
1318 if let TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } = base_ty_resolved
1322 {
1323 indirection = Indirection::Place;
1324 }
1325
1326 let (base_container_ty_handle, base_is_pointer) = match *base_ty_resolved {
1329 TypeInner::Pointer { base, space: _ } => {
1330 base_ty_resolved = &module.types[base].inner;
1331 (Some(base), true)
1332 }
1333 _ => (base_ty_res.handle(), false),
1334 };
1335
1336 match *base_ty_resolved {
1337 TypeInner::Vector { .. } => ra::Expr::Method(
1338 Box::new(self.expr_ast_with_indirection(base, expr_ctx, indirection)?),
1339 Cow::Borrowed(["x", "y", "z", "w"][index as usize]),
1340 vec![],
1341 ),
1342 TypeInner::Matrix { .. }
1343 | TypeInner::Array { .. }
1344 | TypeInner::BindingArray { .. }
1345 | TypeInner::ValuePointer { .. } => ra::Expr::Index(
1346 Box::new(self.expr_ast_with_indirection(base, expr_ctx, indirection)?),
1347 Box::new(ra::Expr::LitUsize(index)),
1348 ),
1349
1350 TypeInner::Struct { .. } => {
1351 let element_type_is_scalar = if base_is_pointer {
1356 result_ty.pointer_base_type().is_some_and(|res| {
1358 matches!(res.inner_with(&module.types), TypeInner::Scalar(_))
1359 })
1360 } else {
1361 matches!(result_ty, TypeInner::Scalar(_))
1362 };
1363 let ty = base_container_ty_handle.unwrap();
1366 if element_type_is_scalar {
1367 ra::Expr::call_rt(
1368 ra::RtItem::Scalar,
1369 [ra::Expr::NamedField(
1370 Box::new(self.expr_ast_with_indirection(
1371 base,
1372 expr_ctx,
1373 indirection,
1374 )?),
1375 self.names[&NameKey::StructMember(ty, index)].clone(),
1376 )],
1377 )
1378 } else {
1379 ra::Expr::NamedField(
1380 Box::new(self.expr_ast_with_indirection(
1381 base,
1382 expr_ctx,
1383 indirection,
1384 )?),
1385 self.names[&NameKey::StructMember(ty, index)].clone(),
1386 )
1387 }
1388 }
1389 ref other => unreachable!("cannot index into a {other:?}"),
1390 }
1391 }
1392 Expression::ImageSample { .. } => {
1393 self.unimplemented_expr("texture sampling (other than textureLoad)")?
1394 }
1395 Expression::ImageQuery { image, query } => {
1396 let image_expr =
1397 self.expr_ast_with_indirection(image, expr_ctx, Indirection::Ref)?;
1398 match query {
1399 naga::ImageQuery::Size { level } => ra::Expr::call_rt(
1400 ra::RtItem::TextureDimensions,
1401 [
1402 image_expr,
1403 if let Some(level) = level {
1404 ra::Expr::call_rt(
1405 ra::RtItem::ScalarIntoInner,
1406 [self.translate_expr(level, expr_ctx)?],
1407 )
1408 } else {
1409 ra::Expr::LitI32(0)
1410 },
1411 ],
1412 ),
1413 naga::ImageQuery::NumLevels => ra::Expr::call_rt(
1414 ra::RtItem::TextureNzToScalar,
1415 [ra::Expr::call_rt(
1416 ra::RtItem::TextureNumLevels,
1417 [image_expr],
1418 )],
1419 ),
1420 naga::ImageQuery::NumLayers => ra::Expr::call_rt(
1421 ra::RtItem::TextureNzToScalar,
1422 [ra::Expr::call_rt(
1423 ra::RtItem::TextureNumLayers,
1424 [image_expr],
1425 )],
1426 ),
1427 naga::ImageQuery::NumSamples => ra::Expr::call_rt(
1428 ra::RtItem::TextureNzToScalar,
1429 [ra::Expr::call_rt(
1430 ra::RtItem::TextureNumSamples,
1431 [image_expr],
1432 )],
1433 ),
1434 }
1435 }
1436 Expression::ImageLoad {
1437 image,
1438 coordinate,
1439 array_index,
1440 sample,
1441 level,
1442 } => ra::Expr::call_rt(
1443 ra::RtItem::TextureLoad,
1444 [
1445 self.expr_ast_with_indirection(image, expr_ctx, Indirection::Ref)?,
1446 self.translate_expr(coordinate, expr_ctx)?,
1447 if let Some(array_index) = array_index {
1448 ra::Expr::call_rt(
1449 ra::RtItem::ScalarIntoInner,
1450 [self.translate_expr(array_index, expr_ctx)?],
1451 )
1452 } else {
1453 ra::Expr::LitI32(0)
1454 },
1455 if let Some(sample) = sample {
1456 ra::Expr::call_rt(
1457 ra::RtItem::ScalarIntoInner,
1458 [self.translate_expr(sample, expr_ctx)?],
1459 )
1460 } else {
1461 ra::Expr::LitI32(0)
1462 },
1463 if let Some(level) = level {
1464 ra::Expr::call_rt(
1465 ra::RtItem::ScalarIntoInner,
1466 [self.translate_expr(level, expr_ctx)?],
1467 )
1468 } else {
1469 ra::Expr::LitI32(0)
1470 },
1471 ],
1472 ),
1473 Expression::GlobalVariable(handle) => {
1474 let global = &module.global_variables[handle];
1475
1476 indirection = match global.space {
1482 naga::AddressSpace::Handle => Indirection::Ordinary,
1483 _ => Indirection::Place,
1484 };
1485
1486 ra::Expr::NamedField(
1487 Box::new(self.config.global_field_access_expr(global)),
1488 self.names[&NameKey::GlobalVariable(handle)].clone(),
1489 )
1490 }
1491
1492 Expression::As {
1493 expr,
1494 kind: to_kind,
1495 convert: to_width,
1496 } => {
1497 use naga::ScalarKind as Sk;
1498 use naga::TypeInner as Ti;
1499
1500 let input_type = expr_ctx.resolve_type(expr);
1501 let rust_expr = self.translate_expr(expr, expr_ctx)?;
1502
1503 match (input_type, to_kind, to_width) {
1504 (
1505 Ti::Vector { size: _, scalar: _ } | Ti::Scalar(_),
1506 to_kind,
1507 Some(to_width),
1508 ) => ra::Expr::Method(
1509 Box::new(rust_expr),
1510 Cow::Borrowed(match (to_kind, to_width) {
1511 (Sk::Sint, 4) => "cast_elem_as_i32",
1512 (Sk::Sint, 8) => "cast_elem_as_i64",
1513 (Sk::Uint, 4) => "cast_elem_as_u32",
1514 (Sk::Uint, 8) => "cast_elem_as_u64",
1515 (Sk::Float, 4) => "cast_elem_as_f32",
1516 (Sk::Float, 8) => "cast_elem_as_f64",
1517 _ => panic!(
1518 "unimplemented cast of vector to kind {to_kind:?} width {to_width:?}"
1519 ),
1520 }),
1521 vec![],
1522 ),
1523 _ => panic!(
1524 "unimplemented cast {input_type:?} to kind {to_kind:?} width {to_width:?}"
1525 ),
1526 }
1527 }
1528 Expression::Load { pointer } => {
1529 self.expr_ast_with_indirection(pointer, expr_ctx, Indirection::Place)?
1530 }
1531 Expression::LocalVariable(handle) => {
1532 indirection = Indirection::Place;
1536
1537 ra::Expr::Ident(self.names[&expr_ctx.expect_func_ctx().name_key(handle)].clone())
1538 }
1539 Expression::ArrayLength(expr) => ra::Expr::Method(
1540 Box::new(self.translate_expr(expr, expr_ctx)?),
1541 Cow::Borrowed("len"),
1542 vec![],
1543 ),
1544
1545 Expression::Math {
1546 fun,
1547 arg: first_arg,
1548 arg1,
1549 arg2,
1550 arg3,
1551 } => ra::Expr::Method(
1552 Box::new(self.translate_expr(first_arg, expr_ctx)?),
1553 Cow::Borrowed(conv::math_function_to_method(fun)),
1554 self.translate_exprs(
1555 expr_ctx,
1556 [arg1, arg2, arg3].into_iter().flatten(), )?,
1558 ),
1559
1560 Expression::Swizzle {
1561 size,
1562 vector,
1563 pattern,
1564 } => {
1565 let swizzle_method = String::from_iter(
1566 pattern[..size as usize]
1567 .iter()
1568 .map(|&component| back::COMPONENTS[component as usize]),
1569 );
1570 ra::Expr::Method(
1571 Box::new(self.translate_expr(vector, expr_ctx)?),
1572 Cow::Owned(swizzle_method),
1573 vec![],
1574 )
1575 }
1576 Expression::Unary { op, expr } => {
1577 let ctor = match op {
1578 naga::UnaryOperator::Negate => ra::Expr::Negate,
1579 naga::UnaryOperator::LogicalNot => ra::Expr::Not,
1580 naga::UnaryOperator::BitwiseNot => ra::Expr::Not,
1581 };
1582 ctor(Box::new(self.translate_expr(expr, expr_ctx)?))
1583 }
1584
1585 Expression::Select {
1586 condition,
1587 accept,
1588 reject,
1589 } => {
1590 ra::Expr::Method(
1592 Box::new(self.translate_expr(reject, expr_ctx)?),
1593 Cow::Borrowed("select"),
1594 vec![
1595 self.translate_expr(accept, expr_ctx)?,
1596 self.translate_expr(condition, expr_ctx)?,
1597 ],
1598 )
1599 }
1600 Expression::Derivative { .. } => self.unimplemented_expr("derivatives")?,
1601 Expression::Relational { fun, argument } => {
1602 use naga::RelationalFunction as Rf;
1603
1604 match fun {
1605 Rf::All => ra::Expr::Method(
1606 Box::new(self.translate_expr(argument, expr_ctx)?),
1607 Cow::Borrowed("all"),
1608 vec![],
1609 ),
1610 Rf::Any => ra::Expr::Method(
1611 Box::new(self.translate_expr(argument, expr_ctx)?),
1612 Cow::Borrowed("any"),
1613 vec![],
1614 ),
1615 Rf::IsNan => self.unimplemented_expr("IsNan")?,
1616 Rf::IsInf => self.unimplemented_expr("IsInf")?,
1617 }
1618 }
1619 Expression::RayQueryGetIntersection { .. }
1620 | Expression::RayQueryVertexPositions { .. }
1621 | Expression::CooperativeLoad { .. }
1622 | Expression::CooperativeMultiplyAdd { .. } => unreachable!("unsupported feature"),
1623 Expression::CallResult(_)
1625 | Expression::AtomicResult { .. }
1626 | Expression::RayQueryProceedResult
1627 | Expression::SubgroupBallotResult
1628 | Expression::SubgroupOperationResult { .. }
1629 | Expression::WorkGroupUniformLoadResult { .. } => {
1630 unreachable!("nothing to do here, since call expression already cached")
1631 }
1632 };
1633
1634 Ok((rust_expr, indirection))
1635 }
1636
1637 fn constructor_expression_ast(
1643 &self,
1644 ty: Handle<naga::Type>,
1645 components: &[Handle<Expression>],
1646 expr_ctx: &ExpressionCtx<'_>,
1647 ) -> Result<ra::Expr, Error> {
1648 use naga::VectorSize::{Bi, Quad, Tri};
1649
1650 let translated_components = components
1651 .iter()
1652 .map(|&component| self.translate_expr(component, expr_ctx))
1653 .collect::<Result<Vec<ra::Expr>, Error>>()?;
1654
1655 let ctor_name: &'static str = match expr_ctx.types()[ty].inner {
1656 TypeInner::Vector { size, scalar: _ } => {
1657 let arg_sizes: ArrayVec<u8, 4> = components
1661 .iter()
1662 .map(|&component_expr| match *expr_ctx.resolve_type(component_expr) {
1663 TypeInner::Scalar(_) => 1,
1664 TypeInner::Vector { size, .. } => size as u8,
1665 ref t => unreachable!(
1666 "vector constructor argument should be a scalar or vector, not {t:?}"
1667 ),
1668 })
1669 .collect();
1670
1671 match (size, &*arg_sizes) {
1672 (Bi, [1, 1]) => "from_scalars",
1673 (Bi, [2]) => "from",
1674 (Tri, [1, 1, 1]) => "from_scalars",
1675 (Tri, [1, 2]) => "new_12",
1676 (Tri, [2, 1]) => "new_21",
1677 (Quad, [1, 1, 1, 1]) => "from_scalars",
1678 (Quad, [1, 1, 2]) => "new_112",
1679 (Quad, [1, 2, 1]) => "new_121",
1680 (Quad, [2, 1, 1]) => "new_211",
1681 (Quad, [2, 2]) => "new_22",
1682 (Quad, [1, 3]) => "new_13",
1683 (Quad, [3, 1]) => "new_31",
1684 (Quad, [4]) => "from",
1685 _ => unreachable!("vector constructor given too many components {arg_sizes:?}"),
1686 }
1687 }
1688
1689 TypeInner::Array {
1690 base: _,
1691 size,
1692 stride: _,
1693 } => {
1694 assert!(matches!(size, naga::ArraySize::Constant(_)));
1695 return Ok(ra::Expr::Array(translated_components));
1696 }
1697
1698 _ => "new",
1701 };
1702
1703 Ok(ra::Expr::Call(
1704 Box::new(ra::Expr::QualifiedPath(
1705 self.type_ast(expr_ctx.module(), ty, TypeTranslation::Simd)?,
1706 ctor_name,
1707 )),
1708 translated_components,
1709 ))
1710 }
1711
1712 pub(super) fn type_ast(
1716 &self,
1717 module: &Module,
1718 type_handle: Handle<naga::Type>,
1719 type_translation: TypeTranslation,
1720 ) -> Result<ra::Type, Error> {
1721 match module.types[type_handle].inner {
1722 TypeInner::Struct { .. } => Ok(ra::Type::User(
1723 self.names[&NameKey::Type(type_handle)].clone(),
1724 ra::Generics::None,
1725 )),
1726 ref other => self.type_ast_inner(module, other, type_translation),
1727 }
1728 }
1729
1730 fn type_ast_inner(
1731 &self,
1732 module: &Module,
1733 inner: &TypeInner,
1734 type_translation: TypeTranslation,
1735 ) -> Result<ra::Type, Error> {
1736 Ok(match *inner {
1737 TypeInner::Vector { size, scalar } => {
1738 ra::Type::RtGen(ra::RtGen::vector(size), scalar.try_into()?)
1739 }
1740 TypeInner::Matrix {
1741 columns,
1742 rows,
1743 scalar,
1744 } => ra::Type::RtGen(ra::RtGen::matrix(columns, rows), scalar.try_into()?),
1745 TypeInner::Scalar(scalar) => match type_translation {
1746 TypeTranslation::RustScalar => ra::Type::BareScalar(scalar.try_into()?),
1747 TypeTranslation::ShaderScalar | TypeTranslation::Simd => {
1748 ra::Type::RtGen(ra::RtGen::Scalar, scalar.try_into()?)
1749 }
1750 },
1751
1752 TypeInner::Sampler { comparison: false } => ra::Type::Sampler,
1753 TypeInner::Sampler { comparison: true } => ra::Type::SamplerComparison,
1754 TypeInner::Image {
1755 dim,
1756 arrayed,
1757 class,
1758 } => {
1759 let (scalar_type, multisampled): (ra::Scalar, bool) = match class {
1760 naga::ImageClass::Sampled { kind, multi } => (
1761 match kind {
1762 naga::ScalarKind::Sint => ra::Scalar::I32,
1763 naga::ScalarKind::Uint => ra::Scalar::U32,
1764 naga::ScalarKind::Float => ra::Scalar::F32,
1765 naga::ScalarKind::Bool => ra::Scalar::Bool,
1766 naga::ScalarKind::AbstractInt | naga::ScalarKind::AbstractFloat => {
1767 unreachable!(
1768 "abstract types should not appear in IR presented to backends"
1769 )
1770 }
1771 },
1772 multi,
1773 ),
1774 naga::ImageClass::Depth { multi } => (ra::Scalar::F32, multi),
1775 naga::ImageClass::External => (ra::Scalar::F32, false),
1776 naga::ImageClass::Storage { .. } => {
1777 return Err(Error::Unimplemented("storage texture types".into()));
1778 }
1779 };
1780 ra::Type::Texture {
1781 dim,
1782 multisampled,
1783 arrayed,
1784 storage_type: Box::new(ra::Type::Ptr(
1785 ra::PtrKind::Shared(Some("g")),
1786 Box::new(ra::Type::DynTextureRead {
1797 dim,
1798 scalar: scalar_type,
1799 }),
1800 )),
1801 }
1802 }
1803 TypeInner::Atomic(scalar) => ra::Type::Atomic(scalar.try_into()?),
1804 TypeInner::Array {
1805 base,
1806 size,
1807 stride: _,
1808 } => {
1809 let element_type = Box::new(self.type_ast(module, base, type_translation)?);
1810 match size {
1811 naga::ArraySize::Constant(size) => ra::Type::Array(element_type, size.get()),
1812 naga::ArraySize::Pending(_handle) => {
1813 return Err(Error::Unimplemented("override array size".into()));
1814 }
1815 naga::ArraySize::Dynamic => ra::Type::Slice(element_type),
1816 }
1817 }
1818 TypeInner::BindingArray { .. } => {
1819 return Err(Error::Unimplemented("binding array".into()));
1820 }
1821 TypeInner::Pointer {
1822 base,
1823 space: pointee_space,
1824 } => ra::Type::Ptr(
1825 if self.config.flags.contains(WriterFlags::RAW_POINTERS) {
1826 ra::PtrKind::RawMut
1827 } else {
1828 ra::PtrKind::Exclusive(None)
1829 },
1830 Box::new(self.type_ast(module, base, TypeTranslation::from(pointee_space))?),
1831 ),
1832 TypeInner::ValuePointer {
1833 size: _,
1834 scalar: _,
1835 space: _,
1836 } => todo!(),
1837 TypeInner::Struct { .. } => {
1838 unreachable!("should only see a struct by name");
1839 }
1840 TypeInner::AccelerationStructure { .. } => {
1841 return Err(Error::Unimplemented("type AccelerationStructure".into()));
1842 }
1843 TypeInner::RayQuery { .. } => {
1844 return Err(Error::Unimplemented("type RayQuery".into()));
1845 }
1846 TypeInner::CooperativeMatrix { .. } => {
1847 return Err(Error::Unimplemented("type CooperativeMatrix".into()));
1848 }
1849 })
1850 }
1851
1852 fn translate_global_variable_to_struct_field(
1853 &self,
1854 module: &Module,
1855 global: &naga::GlobalVariable,
1856 handle: Handle<naga::GlobalVariable>,
1857 ) -> Result<ra::Field, Error> {
1858 let &naga::GlobalVariable {
1859 name: _, space,
1861 binding: _, ty,
1863 init: _,
1864 memory_decorations: _, } = global;
1866
1867 Ok(ra::Field {
1876 attributes: if let Some(naga::ResourceBinding { group, binding }) = global.binding {
1877 vec![ra::Attribute::Doc(format!(
1878 "group({group}) binding({binding})"
1879 ))]
1880 } else {
1881 vec![]
1882 },
1883 visibility: self.visibility(),
1884 name: self.names[&NameKey::GlobalVariable(handle)].clone(),
1885 ty: self.type_ast(module, ty, TypeTranslation::from(space))?,
1886 })
1887 }
1888 fn global_variable_as_field_initializer_expr(
1889 &self,
1890 module: &Module,
1891 info: &ModuleInfo,
1892 global: &naga::GlobalVariable,
1893 ) -> Result<ra::Expr, Error> {
1894 if let Some(init) = global.init {
1895 self.translate_expr(
1896 init,
1897 &ExpressionCtx::Global {
1898 expressions: &module.global_expressions,
1899 module,
1900 module_info: info,
1901 },
1902 )
1903 } else {
1904 Ok(ra::Expr::call_rt(ra::RtItem::ZeroFn, []))
1905 }
1906 }
1907
1908 fn translate_global_constant(
1910 &self,
1911 module: &Module,
1912 info: &ModuleInfo,
1913 handle: Handle<naga::Constant>,
1914 ) -> Result<ra::ConstItem, Error> {
1915 let name = self.names[&NameKey::Constant(handle)].clone();
1916 let visibility = self.visibility();
1917 let ty = self.type_ast(
1918 module,
1919 module.constants[handle].ty,
1920 TypeTranslation::ShaderScalar,
1921 )?;
1922 let init = module.constants[handle].init;
1923
1924 Ok(ra::ConstItem {
1925 attributes: vec![ra::Attribute::AllowNonUpperCaseGlobals],
1926 visibility,
1927 name,
1928 ty,
1929 value: self.translate_expr(
1930 init,
1931 &ExpressionCtx::Global {
1932 expressions: &module.global_expressions,
1933 module,
1934 module_info: info,
1935 },
1936 )?,
1937 })
1938 }
1939
1940 fn unimplemented_expr(&self, unimplemented_feature: &'static str) -> Result<ra::Expr, Error> {
1943 assert!(
1944 !unimplemented_feature.contains(['{', '}']),
1945 "escaping format strings not supported yet"
1946 );
1947
1948 if self.config.flags.contains(WriterFlags::ALLOW_UNIMPLEMENTED) {
1949 Ok(ra::Expr::FormatLikeMacro(
1950 "unimplemented",
1952 alloc::format!(
1953 "this shader function contains a feature which \
1954 cannot yet be translated to Rust, {unimplemented_feature}"
1955 ),
1956 ))
1957 } else {
1958 Err(Error::Unimplemented(unimplemented_feature.into()))
1959 }
1960 }
1961
1962 fn unimplemented_stmt(
1963 &self,
1964 unimplemented_feature: &'static str,
1965 ) -> Result<ra::Statement, Error> {
1966 Ok(ra::Statement::Expr(
1967 self.unimplemented_expr(unimplemented_feature)?,
1968 ))
1969 }
1970
1971 fn visibility(&self) -> ra::Visibility {
1972 if self.config.flags.contains(WriterFlags::PUBLIC) {
1973 ra::Visibility::Public
1974 } else {
1975 ra::Visibility::Private
1976 }
1977 }
1978}