1use alloc::{
2 borrow::ToOwned,
3 boxed::Box,
4 format,
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::num::NonZeroU32;
9
10use crate::front::wgsl::error::{Error, ExpectedToken, InvalidAssignmentType};
11use crate::front::wgsl::index::Index;
12use crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;
13use crate::front::wgsl::parse::number::Number;
14use crate::front::wgsl::parse::{ast, conv};
15use crate::front::wgsl::Result;
16use crate::front::Typifier;
17use crate::{
18 common::wgsl::{TryToWgsl, TypeContext},
19 compact::KeepUnused,
20};
21use crate::{common::ForDebugWithTypes, proc::LayoutErrorInner};
22use crate::{ir, proc};
23use crate::{Arena, FastHashMap, FastIndexMap, Handle, Span};
24
25use construction::Constructor;
26use template_list::TemplateListIter;
27
28mod construction;
29mod conversion;
30mod template_list;
31
32macro_rules! resolve_inner {
46 ($ctx:ident, $expr:expr) => {{
47 $ctx.grow_types($expr)?;
48 $ctx.typifier()[$expr].inner_with(&$ctx.module.types)
49 }};
50}
51pub(super) use resolve_inner;
52
53macro_rules! resolve_inner_binary {
61 ($ctx:ident, $left:expr, $right:expr) => {{
62 $ctx.grow_types($left)?;
63 $ctx.grow_types($right)?;
64 (
65 $ctx.typifier()[$left].inner_with(&$ctx.module.types),
66 $ctx.typifier()[$right].inner_with(&$ctx.module.types),
67 )
68 }};
69}
70
71macro_rules! resolve {
81 ($ctx:ident, $expr:expr) => {{
82 let expr = $expr;
83 $ctx.grow_types(expr)?;
84 &$ctx.typifier()[expr]
85 }};
86}
87pub(super) use resolve;
88
89pub struct GlobalContext<'source, 'temp, 'out> {
91 enable_extensions: EnableExtensions,
92
93 ast_expressions: &'temp Arena<ast::Expression<'source>>,
95
96 globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
100
101 module: &'out mut ir::Module,
103
104 const_typifier: &'temp mut Typifier,
105
106 layouter: &'temp mut proc::Layouter,
107
108 global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
109}
110
111impl<'source> GlobalContext<'source, '_, '_> {
112 const fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {
113 ExpressionContext {
114 enable_extensions: self.enable_extensions,
115 ast_expressions: self.ast_expressions,
116 globals: self.globals,
117 module: self.module,
118 const_typifier: self.const_typifier,
119 layouter: self.layouter,
120 expr_type: ExpressionContextType::Constant(None),
121 global_expression_kind_tracker: self.global_expression_kind_tracker,
122 }
123 }
124
125 const fn as_override(&mut self) -> ExpressionContext<'source, '_, '_> {
126 ExpressionContext {
127 enable_extensions: self.enable_extensions,
128 ast_expressions: self.ast_expressions,
129 globals: self.globals,
130 module: self.module,
131 const_typifier: self.const_typifier,
132 layouter: self.layouter,
133 expr_type: ExpressionContextType::Override,
134 global_expression_kind_tracker: self.global_expression_kind_tracker,
135 }
136 }
137
138 fn ensure_type_exists(
139 &mut self,
140 name: Option<String>,
141 inner: ir::TypeInner,
142 ) -> Handle<ir::Type> {
143 self.module
144 .types
145 .insert(ir::Type { inner, name }, Span::UNDEFINED)
146 }
147}
148
149pub struct StatementContext<'source, 'temp, 'out> {
151 enable_extensions: EnableExtensions,
152
153 ast_expressions: &'temp Arena<ast::Expression<'source>>,
159
160 globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
164
165 local_table:
180 &'temp mut FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>,
181
182 const_typifier: &'temp mut Typifier,
183 typifier: &'temp mut Typifier,
184 layouter: &'temp mut proc::Layouter,
185 function: &'out mut ir::Function,
186 named_expressions: &'out mut FastIndexMap<Handle<ir::Expression>, (String, Span)>,
189 module: &'out mut ir::Module,
190
191 local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
201 global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
202}
203
204impl<'a, 'temp> StatementContext<'a, 'temp, '_> {
205 const fn as_const<'t>(
206 &'t mut self,
207 block: &'t mut ir::Block,
208 emitter: &'t mut proc::Emitter,
209 ) -> ExpressionContext<'a, 't, 't>
210 where
211 'temp: 't,
212 {
213 ExpressionContext {
214 enable_extensions: self.enable_extensions,
215 globals: self.globals,
216 ast_expressions: self.ast_expressions,
217 const_typifier: self.const_typifier,
218 layouter: self.layouter,
219 global_expression_kind_tracker: self.global_expression_kind_tracker,
220 module: self.module,
221 expr_type: ExpressionContextType::Constant(Some(LocalExpressionContext {
222 local_table: self.local_table,
223 function: self.function,
224 block,
225 emitter,
226 typifier: self.typifier,
227 local_expression_kind_tracker: self.local_expression_kind_tracker,
228 })),
229 }
230 }
231
232 const fn as_expression<'t>(
233 &'t mut self,
234 block: &'t mut ir::Block,
235 emitter: &'t mut proc::Emitter,
236 ) -> ExpressionContext<'a, 't, 't>
237 where
238 'temp: 't,
239 {
240 ExpressionContext {
241 enable_extensions: self.enable_extensions,
242 globals: self.globals,
243 ast_expressions: self.ast_expressions,
244 const_typifier: self.const_typifier,
245 layouter: self.layouter,
246 global_expression_kind_tracker: self.global_expression_kind_tracker,
247 module: self.module,
248 expr_type: ExpressionContextType::Runtime(LocalExpressionContext {
249 local_table: self.local_table,
250 function: self.function,
251 block,
252 emitter,
253 typifier: self.typifier,
254 local_expression_kind_tracker: self.local_expression_kind_tracker,
255 }),
256 }
257 }
258
259 #[allow(dead_code)]
260 const fn as_global(&mut self) -> GlobalContext<'a, '_, '_> {
261 GlobalContext {
262 enable_extensions: self.enable_extensions,
263 ast_expressions: self.ast_expressions,
264 globals: self.globals,
265 module: self.module,
266 const_typifier: self.const_typifier,
267 layouter: self.layouter,
268 global_expression_kind_tracker: self.global_expression_kind_tracker,
269 }
270 }
271
272 fn invalid_assignment_type(&self, expr: Handle<ir::Expression>) -> InvalidAssignmentType {
273 if let Some(&(_, span)) = self.named_expressions.get(&expr) {
274 InvalidAssignmentType::ImmutableBinding(span)
275 } else {
276 match self.function.expressions[expr] {
277 ir::Expression::Swizzle { .. } => InvalidAssignmentType::Swizzle,
278 ir::Expression::Access { base, .. } => self.invalid_assignment_type(base),
279 ir::Expression::AccessIndex { base, .. } => self.invalid_assignment_type(base),
280 _ => InvalidAssignmentType::Other,
281 }
282 }
283 }
284}
285
286pub struct LocalExpressionContext<'temp, 'out> {
287 local_table: &'temp FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>,
292
293 function: &'out mut ir::Function,
294 block: &'temp mut ir::Block,
295 emitter: &'temp mut proc::Emitter,
296 typifier: &'temp mut Typifier,
297
298 local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
303}
304
305pub enum ExpressionContextType<'temp, 'out> {
307 Runtime(LocalExpressionContext<'temp, 'out>),
314
315 Constant(Option<LocalExpressionContext<'temp, 'out>>),
322
323 Override,
330}
331
332pub struct ExpressionContext<'source, 'temp, 'out> {
370 enable_extensions: EnableExtensions,
371
372 ast_expressions: &'temp Arena<ast::Expression<'source>>,
374
375 globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
379
380 module: &'out mut ir::Module,
384
385 const_typifier: &'temp mut Typifier,
389 layouter: &'temp mut proc::Layouter,
390 global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
391
392 expr_type: ExpressionContextType<'temp, 'out>,
395}
396
397impl TypeContext for ExpressionContext<'_, '_, '_> {
398 fn lookup_type(&self, handle: Handle<ir::Type>) -> &ir::Type {
399 &self.module.types[handle]
400 }
401
402 fn type_name(&self, handle: Handle<ir::Type>) -> &str {
403 self.module.types[handle]
404 .name
405 .as_deref()
406 .unwrap_or("{anonymous type}")
407 }
408
409 fn write_override<W: core::fmt::Write>(
410 &self,
411 handle: Handle<ir::Override>,
412 out: &mut W,
413 ) -> core::fmt::Result {
414 match self.module.overrides[handle].name {
415 Some(ref name) => out.write_str(name),
416 None => write!(out, "{{anonymous override {handle:?}}}"),
417 }
418 }
419
420 fn write_unnamed_struct<W: core::fmt::Write>(
421 &self,
422 _: &ir::TypeInner,
423 _: &mut W,
424 ) -> core::fmt::Result {
425 unreachable!("the WGSL front end should always know the type name");
426 }
427}
428
429impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
430 const fn is_runtime(&self) -> bool {
431 match self.expr_type {
432 ExpressionContextType::Runtime(_) => true,
433 ExpressionContextType::Constant(_) | ExpressionContextType::Override => false,
434 }
435 }
436
437 #[allow(dead_code)]
438 const fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {
439 ExpressionContext {
440 enable_extensions: self.enable_extensions,
441 globals: self.globals,
442 ast_expressions: self.ast_expressions,
443 const_typifier: self.const_typifier,
444 layouter: self.layouter,
445 module: self.module,
446 expr_type: ExpressionContextType::Constant(match self.expr_type {
447 ExpressionContextType::Runtime(ref mut local_expression_context)
448 | ExpressionContextType::Constant(Some(ref mut local_expression_context)) => {
449 Some(LocalExpressionContext {
450 local_table: local_expression_context.local_table,
451 function: local_expression_context.function,
452 block: local_expression_context.block,
453 emitter: local_expression_context.emitter,
454 typifier: local_expression_context.typifier,
455 local_expression_kind_tracker: local_expression_context
456 .local_expression_kind_tracker,
457 })
458 }
459 ExpressionContextType::Constant(None) | ExpressionContextType::Override => None,
460 }),
461 global_expression_kind_tracker: self.global_expression_kind_tracker,
462 }
463 }
464
465 const fn as_global(&mut self) -> GlobalContext<'source, '_, '_> {
466 GlobalContext {
467 enable_extensions: self.enable_extensions,
468 ast_expressions: self.ast_expressions,
469 globals: self.globals,
470 module: self.module,
471 const_typifier: self.const_typifier,
472 layouter: self.layouter,
473 global_expression_kind_tracker: self.global_expression_kind_tracker,
474 }
475 }
476
477 const fn as_const_evaluator(&mut self) -> proc::ConstantEvaluator<'_> {
478 match self.expr_type {
479 ExpressionContextType::Runtime(ref mut rctx) => {
480 proc::ConstantEvaluator::for_wgsl_function(
481 self.module,
482 &mut rctx.function.expressions,
483 rctx.local_expression_kind_tracker,
484 self.layouter,
485 rctx.emitter,
486 rctx.block,
487 false,
488 )
489 }
490 ExpressionContextType::Constant(Some(ref mut rctx)) => {
491 proc::ConstantEvaluator::for_wgsl_function(
492 self.module,
493 &mut rctx.function.expressions,
494 rctx.local_expression_kind_tracker,
495 self.layouter,
496 rctx.emitter,
497 rctx.block,
498 true,
499 )
500 }
501 ExpressionContextType::Constant(None) => proc::ConstantEvaluator::for_wgsl_module(
502 self.module,
503 self.global_expression_kind_tracker,
504 self.layouter,
505 false,
506 ),
507 ExpressionContextType::Override => proc::ConstantEvaluator::for_wgsl_module(
508 self.module,
509 self.global_expression_kind_tracker,
510 self.layouter,
511 true,
512 ),
513 }
514 }
515
516 const fn as_diagnostic_display<T>(
522 &self,
523 value: T,
524 ) -> crate::common::DiagnosticDisplay<(T, proc::GlobalCtx<'_>)> {
525 let ctx = self.module.to_ctx();
526 crate::common::DiagnosticDisplay((value, ctx))
527 }
528
529 fn append_expression(
530 &mut self,
531 expr: ir::Expression,
532 span: Span,
533 ) -> Result<'source, Handle<ir::Expression>> {
534 let mut eval = self.as_const_evaluator();
535 eval.try_eval_and_append(expr, span)
536 .map_err(|e| Box::new(Error::ConstantEvaluatorError(e.into(), span)))
537 }
538
539 fn get_const_val<T: TryFrom<crate::Literal, Error = proc::ConstValueError>>(
540 &self,
541 handle: Handle<ir::Expression>,
542 ) -> core::result::Result<T, proc::ConstValueError> {
543 match self.expr_type {
544 ExpressionContextType::Runtime(ref ctx) => {
545 if !ctx.local_expression_kind_tracker.is_const(handle) {
546 return Err(proc::ConstValueError::NonConst);
547 }
548
549 self.module
550 .to_ctx()
551 .get_const_val_from(handle, &ctx.function.expressions)
552 }
553 ExpressionContextType::Constant(Some(ref ctx)) => {
554 assert!(ctx.local_expression_kind_tracker.is_const(handle));
555 self.module
556 .to_ctx()
557 .get_const_val_from(handle, &ctx.function.expressions)
558 }
559 ExpressionContextType::Constant(None) => self.module.to_ctx().get_const_val(handle),
560 ExpressionContextType::Override => Err(proc::ConstValueError::NonConst),
561 }
562 }
563
564 fn is_const(&self, handle: Handle<ir::Expression>) -> bool {
566 use ExpressionContextType as Ect;
567 match self.expr_type {
568 Ect::Runtime(ref ctx) | Ect::Constant(Some(ref ctx)) => {
569 ctx.local_expression_kind_tracker.is_const(handle)
570 }
571 Ect::Constant(None) | Ect::Override => {
572 self.global_expression_kind_tracker.is_const(handle)
573 }
574 }
575 }
576
577 fn get_expression_span(&self, handle: Handle<ir::Expression>) -> Span {
578 match self.expr_type {
579 ExpressionContextType::Runtime(ref ctx)
580 | ExpressionContextType::Constant(Some(ref ctx)) => {
581 ctx.function.expressions.get_span(handle)
582 }
583 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
584 self.module.global_expressions.get_span(handle)
585 }
586 }
587 }
588
589 const fn typifier(&self) -> &Typifier {
590 match self.expr_type {
591 ExpressionContextType::Runtime(ref ctx)
592 | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,
593 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
594 self.const_typifier
595 }
596 }
597 }
598
599 fn get(&self, handle: Handle<crate::Expression>) -> &crate::Expression {
600 match self.expr_type {
601 ExpressionContextType::Runtime(ref ctx)
602 | ExpressionContextType::Constant(Some(ref ctx)) => &ctx.function.expressions[handle],
603 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
604 &self.module.global_expressions[handle]
605 }
606 }
607 }
608
609 fn local(
610 &mut self,
611 local: &Handle<ast::Local>,
612 span: Span,
613 ) -> Result<'source, Typed<Handle<ir::Expression>>> {
614 match self.expr_type {
615 ExpressionContextType::Runtime(ref ctx) => Ok(ctx.local_table[local].runtime()),
616 ExpressionContextType::Constant(Some(ref ctx)) => ctx.local_table[local]
617 .const_time()
618 .ok_or(Box::new(Error::UnexpectedOperationInConstContext(span))),
619 _ => Err(Box::new(Error::UnexpectedOperationInConstContext(span))),
620 }
621 }
622
623 fn runtime_expression_ctx(
624 &mut self,
625 span: Span,
626 ) -> Result<'source, &mut LocalExpressionContext<'temp, 'out>> {
627 match self.expr_type {
628 ExpressionContextType::Runtime(ref mut ctx) => Ok(ctx),
629 ExpressionContextType::Constant(_) | ExpressionContextType::Override => {
630 Err(Box::new(Error::UnexpectedOperationInConstContext(span)))
631 }
632 }
633 }
634
635 fn with_nested_runtime_expression_ctx<'a, F, T>(
636 &mut self,
637 span: Span,
638 f: F,
639 ) -> Result<'source, (T, crate::Block)>
640 where
641 for<'t> F: FnOnce(&mut ExpressionContext<'source, 't, 't>) -> Result<'source, T>,
642 {
643 let mut block = crate::Block::new();
644 let rctx = match self.expr_type {
645 ExpressionContextType::Runtime(ref mut rctx) => Ok(rctx),
646 ExpressionContextType::Constant(_) | ExpressionContextType::Override => {
647 Err(Error::UnexpectedOperationInConstContext(span))
648 }
649 }?;
650
651 rctx.block
652 .extend(rctx.emitter.finish(&rctx.function.expressions));
653 rctx.emitter.start(&rctx.function.expressions);
654
655 let nested_rctx = LocalExpressionContext {
656 local_table: rctx.local_table,
657 function: rctx.function,
658 block: &mut block,
659 emitter: rctx.emitter,
660 typifier: rctx.typifier,
661 local_expression_kind_tracker: rctx.local_expression_kind_tracker,
662 };
663 let mut nested_ctx = ExpressionContext {
664 enable_extensions: self.enable_extensions,
665 expr_type: ExpressionContextType::Runtime(nested_rctx),
666 ast_expressions: self.ast_expressions,
667 globals: self.globals,
668 module: self.module,
669 const_typifier: self.const_typifier,
670 layouter: self.layouter,
671 global_expression_kind_tracker: self.global_expression_kind_tracker,
672 };
673 let ret = f(&mut nested_ctx)?;
674
675 block.extend(rctx.emitter.finish(&rctx.function.expressions));
676 rctx.emitter.start(&rctx.function.expressions);
677
678 Ok((ret, block))
679 }
680
681 fn gather_component(
682 &mut self,
683 expr: Handle<ir::Expression>,
684 component_span: Span,
685 gather_span: Span,
686 ) -> Result<'source, ir::SwizzleComponent> {
687 match self.expr_type {
688 ExpressionContextType::Runtime(ref rctx) => {
689 if !rctx.local_expression_kind_tracker.is_const(expr) {
690 return Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
691 component_span,
692 )));
693 }
694
695 let index = self
696 .module
697 .to_ctx()
698 .get_const_val_from::<u32, _>(expr, &rctx.function.expressions)
699 .map_err(|err| match err {
700 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
701 Error::ExpectedConstExprConcreteIntegerScalar(component_span)
702 }
703 proc::ConstValueError::Negative => {
704 Error::ExpectedNonNegative(component_span)
705 }
706 })?;
707 ir::SwizzleComponent::XYZW
708 .get(index as usize)
709 .copied()
710 .ok_or(Box::new(Error::InvalidGatherComponent(component_span)))
711 }
712 ExpressionContextType::Constant(_) | ExpressionContextType::Override => Err(Box::new(
715 Error::UnexpectedOperationInConstContext(gather_span),
716 )),
717 }
718 }
719
720 fn register_type(
731 &mut self,
732 handle: Handle<ir::Expression>,
733 ) -> Result<'source, Handle<ir::Type>> {
734 self.grow_types(handle)?;
735 let typifier = match self.expr_type {
739 ExpressionContextType::Runtime(ref ctx)
740 | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,
741 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
742 &*self.const_typifier
743 }
744 };
745 Ok(typifier.register_type(handle, &mut self.module.types))
746 }
747
748 fn grow_types(&mut self, handle: Handle<ir::Expression>) -> Result<'source, &mut Self> {
769 let empty_arena = Arena::new();
770 let resolve_ctx;
771 let typifier;
772 let expressions;
773 match self.expr_type {
774 ExpressionContextType::Runtime(ref mut ctx)
775 | ExpressionContextType::Constant(Some(ref mut ctx)) => {
776 resolve_ctx = proc::ResolveContext::with_locals(
777 self.module,
778 &ctx.function.local_variables,
779 &ctx.function.arguments,
780 );
781 typifier = &mut *ctx.typifier;
782 expressions = &ctx.function.expressions;
783 }
784 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
785 resolve_ctx = proc::ResolveContext::with_locals(self.module, &empty_arena, &[]);
786 typifier = self.const_typifier;
787 expressions = &self.module.global_expressions;
788 }
789 };
790 typifier
791 .grow(handle, expressions, &resolve_ctx)
792 .map_err(Error::InvalidResolve)?;
793
794 Ok(self)
795 }
796
797 fn image_data(
798 &mut self,
799 image: Handle<ir::Expression>,
800 span: Span,
801 ) -> Result<'source, (ir::ImageClass, bool)> {
802 match *resolve_inner!(self, image) {
803 ir::TypeInner::Image { class, arrayed, .. } => Ok((class, arrayed)),
804 _ => Err(Box::new(Error::BadTexture(span))),
805 }
806 }
807
808 fn prepare_args<'b>(
809 &mut self,
810 args: &'b [Handle<ast::Expression<'source>>],
811 min_args: u32,
812 span: Span,
813 ) -> ArgumentContext<'b, 'source> {
814 ArgumentContext {
815 args: args.iter(),
816 min_args,
817 args_used: 0,
818 total_args: args.len() as u32,
819 span,
820 }
821 }
822
823 fn binary_op_splat(
831 &mut self,
832 op: ir::BinaryOperator,
833 left: &mut Handle<ir::Expression>,
834 right: &mut Handle<ir::Expression>,
835 ) -> Result<'source, ()> {
836 if matches!(
837 op,
838 ir::BinaryOperator::Add
839 | ir::BinaryOperator::Subtract
840 | ir::BinaryOperator::Divide
841 | ir::BinaryOperator::Modulo
842 ) {
843 match resolve_inner_binary!(self, *left, *right) {
844 (&ir::TypeInner::Vector { size, .. }, &ir::TypeInner::Scalar { .. }) => {
845 *right = self.append_expression(
846 ir::Expression::Splat {
847 size,
848 value: *right,
849 },
850 self.get_expression_span(*right),
851 )?;
852 }
853 (&ir::TypeInner::Scalar { .. }, &ir::TypeInner::Vector { size, .. }) => {
854 *left = self.append_expression(
855 ir::Expression::Splat { size, value: *left },
856 self.get_expression_span(*left),
857 )?;
858 }
859 _ => {}
860 }
861 }
862
863 Ok(())
864 }
865
866 fn interrupt_emitter(
871 &mut self,
872 expression: ir::Expression,
873 span: Span,
874 ) -> Result<'source, Handle<ir::Expression>> {
875 match self.expr_type {
876 ExpressionContextType::Runtime(ref mut rctx)
877 | ExpressionContextType::Constant(Some(ref mut rctx)) => {
878 rctx.block
879 .extend(rctx.emitter.finish(&rctx.function.expressions));
880 }
881 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}
882 }
883 let result = self.append_expression(expression, span);
884 match self.expr_type {
885 ExpressionContextType::Runtime(ref mut rctx)
886 | ExpressionContextType::Constant(Some(ref mut rctx)) => {
887 rctx.emitter.start(&rctx.function.expressions);
888 }
889 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}
890 }
891 result
892 }
893
894 fn apply_load_rule(
899 &mut self,
900 expr: Typed<Handle<ir::Expression>>,
901 ) -> Result<'source, Handle<ir::Expression>> {
902 match expr {
903 Typed::Reference(pointer) => {
904 let load = ir::Expression::Load { pointer };
905 let span = self.get_expression_span(pointer);
906 self.append_expression(load, span)
907 }
908 Typed::Plain(handle) => Ok(handle),
909 }
910 }
911
912 fn ensure_type_exists(&mut self, inner: ir::TypeInner) -> Handle<ir::Type> {
913 self.as_global().ensure_type_exists(None, inner)
914 }
915
916 fn enumerant(
927 &self,
928 expr: Handle<ast::Expression<'source>>,
929 ) -> Result<'source, (&'source str, Span)> {
930 let span = self.ast_expressions.get_span(expr);
931 let expr = &self.ast_expressions[expr];
932
933 let ast::Expression::Ident(ref ident) = *expr else {
934 return Err(Box::new(Error::UnexpectedExprForEnumerant(span)));
935 };
936
937 let ast::TemplateElaboratedIdent {
938 ident: ast::IdentExpr::Unresolved(name),
939 ref template_list,
940 ..
941 } = *ident
942 else {
943 return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));
944 };
945
946 if self.globals.get(name).is_some() {
947 return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));
948 }
949
950 if !template_list.is_empty() {
951 return Err(Box::new(Error::UnexpectedTemplate(span)));
952 }
953
954 Ok((name, span))
955 }
956
957 fn var_address_space(
958 &self,
959 template_list: &[Handle<ast::Expression<'source>>],
960 ) -> Result<'source, ir::AddressSpace> {
961 let mut tl = TemplateListIter::new(Span::UNDEFINED, template_list);
962 let mut address_space = tl.maybe_address_space(self)?;
963 if let Some(ref mut address_space) = address_space {
964 tl.maybe_access_mode(address_space, self)?;
965 }
966 tl.finish(self)?;
967 Ok(address_space.unwrap_or(ir::AddressSpace::Handle))
968 }
969}
970
971struct ArgumentContext<'ctx, 'source> {
972 args: core::slice::Iter<'ctx, Handle<ast::Expression<'source>>>,
973 min_args: u32,
974 args_used: u32,
975 total_args: u32,
976 span: Span,
977}
978
979impl<'source> ArgumentContext<'_, 'source> {
980 pub fn finish(self) -> Result<'source, ()> {
981 if self.args.len() == 0 {
982 Ok(())
983 } else {
984 Err(Box::new(Error::WrongArgumentCount {
985 found: self.total_args,
986 expected: self.min_args..self.args_used + 1,
987 span: self.span,
988 }))
989 }
990 }
991
992 pub fn next(&mut self) -> Result<'source, Handle<ast::Expression<'source>>> {
993 match self.args.next().copied() {
994 Some(arg) => {
995 self.args_used += 1;
996 Ok(arg)
997 }
998 None => Err(Box::new(Error::WrongArgumentCount {
999 found: self.total_args,
1000 expected: self.min_args..self.args_used + 1,
1001 span: self.span,
1002 })),
1003 }
1004 }
1005}
1006
1007#[derive(Debug, Copy, Clone)]
1008enum Declared<T> {
1009 Const(T),
1011
1012 Runtime(T),
1014}
1015
1016impl<T> Declared<T> {
1017 fn runtime(self) -> T {
1018 match self {
1019 Declared::Const(t) | Declared::Runtime(t) => t,
1020 }
1021 }
1022
1023 fn const_time(self) -> Option<T> {
1024 match self {
1025 Declared::Const(t) => Some(t),
1026 Declared::Runtime(_) => None,
1027 }
1028 }
1029}
1030
1031#[derive(Debug, Copy, Clone)]
1053enum Typed<T> {
1054 Reference(T),
1056
1057 Plain(T),
1059}
1060
1061impl<T> Typed<T> {
1062 fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> {
1063 match self {
1064 Self::Reference(v) => Typed::Reference(f(v)),
1065 Self::Plain(v) => Typed::Plain(f(v)),
1066 }
1067 }
1068
1069 fn try_map<U, E>(
1070 self,
1071 mut f: impl FnMut(T) -> core::result::Result<U, E>,
1072 ) -> core::result::Result<Typed<U>, E> {
1073 Ok(match self {
1074 Self::Reference(expr) => Typed::Reference(f(expr)?),
1075 Self::Plain(expr) => Typed::Plain(f(expr)?),
1076 })
1077 }
1078
1079 fn ref_or<E>(self, error: E) -> core::result::Result<T, E> {
1080 match self {
1081 Self::Reference(v) => Ok(v),
1082 Self::Plain(_) => Err(error),
1083 }
1084 }
1085}
1086
1087enum Components {
1093 Single(u32),
1094 Swizzle {
1095 size: ir::VectorSize,
1096 pattern: [ir::SwizzleComponent; 4],
1097 },
1098}
1099
1100impl Components {
1101 const fn letter_component(letter: char) -> Option<ir::SwizzleComponent> {
1102 use ir::SwizzleComponent as Sc;
1103 match letter {
1104 'x' | 'r' => Some(Sc::X),
1105 'y' | 'g' => Some(Sc::Y),
1106 'z' | 'b' => Some(Sc::Z),
1107 'w' | 'a' => Some(Sc::W),
1108 _ => None,
1109 }
1110 }
1111
1112 fn single_component(name: &str, name_span: Span) -> Result<'_, u32> {
1113 let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?;
1114 match Self::letter_component(ch) {
1115 Some(sc) => Ok(sc as u32),
1116 None => Err(Box::new(Error::BadAccessor(name_span))),
1117 }
1118 }
1119
1120 fn new(name: &str, name_span: Span) -> Result<'_, Self> {
1124 let size = match name.len() {
1125 1 => return Ok(Components::Single(Self::single_component(name, name_span)?)),
1126 2 => ir::VectorSize::Bi,
1127 3 => ir::VectorSize::Tri,
1128 4 => ir::VectorSize::Quad,
1129 _ => return Err(Box::new(Error::BadAccessor(name_span))),
1130 };
1131
1132 let mut pattern = [ir::SwizzleComponent::X; 4];
1133 for (comp, ch) in pattern.iter_mut().zip(name.chars()) {
1134 *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?;
1135 }
1136
1137 if name.chars().all(|c| matches!(c, 'x' | 'y' | 'z' | 'w'))
1138 || name.chars().all(|c| matches!(c, 'r' | 'g' | 'b' | 'a'))
1139 {
1140 Ok(Components::Swizzle { size, pattern })
1141 } else {
1142 Err(Box::new(Error::BadAccessor(name_span)))
1143 }
1144 }
1145}
1146
1147enum LoweredGlobalDecl {
1149 Function {
1150 handle: Handle<ir::Function>,
1151 must_use: bool,
1152 },
1153 Var(Handle<ir::GlobalVariable>),
1154 Const(Handle<ir::Constant>),
1155 Override(Handle<ir::Override>),
1156 Type(Handle<ir::Type>),
1157 EntryPoint(usize),
1158}
1159
1160enum Texture {
1161 Gather,
1162 GatherCompare,
1163
1164 Sample,
1165 SampleBias,
1166 SampleCompare,
1167 SampleCompareLevel,
1168 SampleGrad,
1169 SampleLevel,
1170 SampleBaseClampToEdge,
1171}
1172
1173impl Texture {
1174 pub fn map(word: &str) -> Option<Self> {
1175 Some(match word {
1176 "textureGather" => Self::Gather,
1177 "textureGatherCompare" => Self::GatherCompare,
1178
1179 "textureSample" => Self::Sample,
1180 "textureSampleBias" => Self::SampleBias,
1181 "textureSampleCompare" => Self::SampleCompare,
1182 "textureSampleCompareLevel" => Self::SampleCompareLevel,
1183 "textureSampleGrad" => Self::SampleGrad,
1184 "textureSampleLevel" => Self::SampleLevel,
1185 "textureSampleBaseClampToEdge" => Self::SampleBaseClampToEdge,
1186 _ => return None,
1187 })
1188 }
1189
1190 pub const fn min_argument_count(&self) -> u32 {
1191 match *self {
1192 Self::Gather => 3,
1193 Self::GatherCompare => 4,
1194
1195 Self::Sample => 3,
1196 Self::SampleBias => 5,
1197 Self::SampleCompare => 5,
1198 Self::SampleCompareLevel => 5,
1199 Self::SampleGrad => 6,
1200 Self::SampleLevel => 5,
1201 Self::SampleBaseClampToEdge => 3,
1202 }
1203 }
1204}
1205
1206enum SubgroupGather {
1207 BroadcastFirst,
1208 Broadcast,
1209 Shuffle,
1210 ShuffleDown,
1211 ShuffleUp,
1212 ShuffleXor,
1213 QuadBroadcast,
1214}
1215
1216impl SubgroupGather {
1217 pub fn map(word: &str) -> Option<Self> {
1218 Some(match word {
1219 "subgroupBroadcastFirst" => Self::BroadcastFirst,
1220 "subgroupBroadcast" => Self::Broadcast,
1221 "subgroupShuffle" => Self::Shuffle,
1222 "subgroupShuffleDown" => Self::ShuffleDown,
1223 "subgroupShuffleUp" => Self::ShuffleUp,
1224 "subgroupShuffleXor" => Self::ShuffleXor,
1225 "quadBroadcast" => Self::QuadBroadcast,
1226 _ => return None,
1227 })
1228 }
1229}
1230
1231enum AbstractRule {
1233 Concretize,
1235
1236 Allow,
1238}
1239
1240#[derive(Debug, Copy, Clone)]
1242enum MustUse {
1243 Yes,
1244 No,
1245}
1246
1247impl From<bool> for MustUse {
1248 fn from(value: bool) -> Self {
1249 if value {
1250 MustUse::Yes
1251 } else {
1252 MustUse::No
1253 }
1254 }
1255}
1256
1257pub struct Lowerer<'source, 'temp> {
1258 index: &'temp Index<'source>,
1259}
1260
1261impl<'source, 'temp> Lowerer<'source, 'temp> {
1262 pub const fn new(index: &'temp Index<'source>) -> Self {
1263 Self { index }
1264 }
1265
1266 pub fn lower(&mut self, tu: ast::TranslationUnit<'source>) -> Result<'source, ir::Module> {
1267 let mut module = ir::Module {
1268 diagnostic_filters: tu.diagnostic_filters,
1269 diagnostic_filter_leaf: tu.diagnostic_filter_leaf,
1270 ..Default::default()
1271 };
1272
1273 let mut ctx = GlobalContext {
1274 enable_extensions: tu.enable_extensions,
1275 ast_expressions: &tu.expressions,
1276 globals: &mut FastHashMap::default(),
1277 module: &mut module,
1278 const_typifier: &mut Typifier::new(),
1279 layouter: &mut proc::Layouter::default(),
1280 global_expression_kind_tracker: &mut proc::ExpressionKindTracker::new(),
1281 };
1282 if !tu.doc_comments.is_empty() {
1283 ctx.module.get_or_insert_default_doc_comments().module =
1284 tu.doc_comments.iter().map(|s| s.to_string()).collect();
1285 }
1286
1287 for decl_handle in self.index.visit_ordered() {
1288 let span = tu.decls.get_span(decl_handle);
1289 let decl = &tu.decls[decl_handle];
1290
1291 match decl.kind {
1292 ast::GlobalDeclKind::Fn(ref f) => {
1293 let lowered_decl = self.function(f, span, &mut ctx)?;
1294 if !f.doc_comments.is_empty() {
1295 match lowered_decl {
1296 LoweredGlobalDecl::Function { handle, .. } => {
1297 ctx.module
1298 .get_or_insert_default_doc_comments()
1299 .functions
1300 .insert(
1301 handle,
1302 f.doc_comments.iter().map(|s| s.to_string()).collect(),
1303 );
1304 }
1305 LoweredGlobalDecl::EntryPoint(index) => {
1306 ctx.module
1307 .get_or_insert_default_doc_comments()
1308 .entry_points
1309 .insert(
1310 index,
1311 f.doc_comments.iter().map(|s| s.to_string()).collect(),
1312 );
1313 }
1314 _ => {}
1315 }
1316 }
1317 ctx.globals.insert(f.name.name, lowered_decl);
1318 }
1319 ast::GlobalDeclKind::Var(ref v) => {
1320 let explicit_ty =
1321 v.ty.as_ref()
1322 .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1323 .transpose()?;
1324
1325 let (ty, initializer) = self.type_and_init(
1326 v.name,
1327 v.init,
1328 explicit_ty,
1329 AbstractRule::Concretize,
1330 &mut ctx.as_override(),
1331 )?;
1332
1333 let binding = if let Some(ref binding) = v.binding {
1334 Some(ir::ResourceBinding {
1335 group: self.const_u32(binding.group, &mut ctx.as_const())?.0,
1336 binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0,
1337 })
1338 } else {
1339 None
1340 };
1341
1342 let space = ctx.as_const().var_address_space(&v.template_list)?;
1343
1344 let handle = ctx.module.global_variables.append(
1345 ir::GlobalVariable {
1346 name: Some(v.name.name.to_string()),
1347 space,
1348 binding,
1349 ty,
1350 init: initializer,
1351 },
1352 span,
1353 );
1354
1355 if !v.doc_comments.is_empty() {
1356 ctx.module
1357 .get_or_insert_default_doc_comments()
1358 .global_variables
1359 .insert(
1360 handle,
1361 v.doc_comments.iter().map(|s| s.to_string()).collect(),
1362 );
1363 }
1364 ctx.globals
1365 .insert(v.name.name, LoweredGlobalDecl::Var(handle));
1366 }
1367 ast::GlobalDeclKind::Const(ref c) => {
1368 let mut ectx = ctx.as_const();
1369
1370 let explicit_ty =
1371 c.ty.as_ref()
1372 .map(|ast| self.resolve_ast_type(ast, &mut ectx))
1373 .transpose()?;
1374
1375 let (ty, init) = self.type_and_init(
1376 c.name,
1377 Some(c.init),
1378 explicit_ty,
1379 AbstractRule::Allow,
1380 &mut ectx,
1381 )?;
1382 let init = init.expect("Global const must have init");
1383
1384 let handle = ctx.module.constants.append(
1385 ir::Constant {
1386 name: Some(c.name.name.to_string()),
1387 ty,
1388 init,
1389 },
1390 span,
1391 );
1392
1393 ctx.globals
1394 .insert(c.name.name, LoweredGlobalDecl::Const(handle));
1395 if !c.doc_comments.is_empty() {
1396 ctx.module
1397 .get_or_insert_default_doc_comments()
1398 .constants
1399 .insert(
1400 handle,
1401 c.doc_comments.iter().map(|s| s.to_string()).collect(),
1402 );
1403 }
1404 }
1405 ast::GlobalDeclKind::Override(ref o) => {
1406 let explicit_ty =
1407 o.ty.as_ref()
1408 .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1409 .transpose()?;
1410
1411 let mut ectx = ctx.as_override();
1412
1413 let (ty, init) = self.type_and_init(
1414 o.name,
1415 o.init,
1416 explicit_ty,
1417 AbstractRule::Concretize,
1418 &mut ectx,
1419 )?;
1420
1421 let id =
1422 o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))
1423 .transpose()?;
1424
1425 let id = if let Some((id, id_span)) = id {
1426 Some(
1427 u16::try_from(id)
1428 .map_err(|_| Error::PipelineConstantIDValue(id_span))?,
1429 )
1430 } else {
1431 None
1432 };
1433
1434 let handle = ctx.module.overrides.append(
1435 ir::Override {
1436 name: Some(o.name.name.to_string()),
1437 id,
1438 ty,
1439 init,
1440 },
1441 span,
1442 );
1443
1444 ctx.globals
1445 .insert(o.name.name, LoweredGlobalDecl::Override(handle));
1446 }
1447 ast::GlobalDeclKind::Struct(ref s) => {
1448 let handle = self.r#struct(s, span, &mut ctx)?;
1449 ctx.globals
1450 .insert(s.name.name, LoweredGlobalDecl::Type(handle));
1451 if !s.doc_comments.is_empty() {
1452 ctx.module
1453 .get_or_insert_default_doc_comments()
1454 .types
1455 .insert(
1456 handle,
1457 s.doc_comments.iter().map(|s| s.to_string()).collect(),
1458 );
1459 }
1460 }
1461 ast::GlobalDeclKind::Type(ref alias) => {
1462 let ty = self.resolve_named_ast_type(
1463 &alias.ty,
1464 alias.name.name.to_string(),
1465 &mut ctx.as_const(),
1466 )?;
1467 ctx.globals
1468 .insert(alias.name.name, LoweredGlobalDecl::Type(ty));
1469 }
1470 ast::GlobalDeclKind::ConstAssert(condition) => {
1471 let condition = self.expression(condition, &mut ctx.as_const())?;
1472
1473 let span = ctx.module.global_expressions.get_span(condition);
1474 match ctx
1475 .module
1476 .to_ctx()
1477 .get_const_val_from(condition, &ctx.module.global_expressions)
1478 {
1479 Ok(true) => Ok(()),
1480 Ok(false) => Err(Error::ConstAssertFailed(span)),
1481 Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
1482 unreachable!()
1483 }
1484 Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
1485 }?;
1486 }
1487 }
1488 }
1489
1490 crate::compact::compact(&mut module, KeepUnused::Yes);
1494
1495 Ok(module)
1496 }
1497
1498 fn type_and_init(
1500 &mut self,
1501 name: ast::Ident<'source>,
1502 init: Option<Handle<ast::Expression<'source>>>,
1503 explicit_ty: Option<Handle<ir::Type>>,
1504 abstract_rule: AbstractRule,
1505 ectx: &mut ExpressionContext<'source, '_, '_>,
1506 ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> {
1507 let ty;
1508 let initializer;
1509 match (init, explicit_ty) {
1510 (Some(init), Some(explicit_ty)) => {
1511 let init = self.expression_for_abstract(init, ectx)?;
1512 let ty_res = proc::TypeResolution::Handle(explicit_ty);
1513 let init = ectx
1514 .try_automatic_conversions(init, &ty_res, name.span)
1515 .map_err(|error| match *error {
1516 Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch {
1517 name: name.span,
1518 expected: e.dest_type,
1519 got: e.source_type,
1520 }),
1521 _ => error,
1522 })?;
1523
1524 let init_ty = ectx.register_type(init)?;
1525 if !ectx.module.compare_types(
1526 &proc::TypeResolution::Handle(explicit_ty),
1527 &proc::TypeResolution::Handle(init_ty),
1528 ) {
1529 return Err(Box::new(Error::InitializationTypeMismatch {
1530 name: name.span,
1531 expected: ectx.type_to_string(explicit_ty),
1532 got: ectx.type_to_string(init_ty),
1533 }));
1534 }
1535 ty = explicit_ty;
1536 initializer = Some(init);
1537 }
1538 (Some(init), None) => {
1539 let mut init = self.expression_for_abstract(init, ectx)?;
1540 if let AbstractRule::Concretize = abstract_rule {
1541 init = ectx.concretize(init)?;
1542 }
1543 ty = ectx.register_type(init)?;
1544 initializer = Some(init);
1545 }
1546 (None, Some(explicit_ty)) => {
1547 ty = explicit_ty;
1548 initializer = None;
1549 }
1550 (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))),
1551 }
1552 Ok((ty, initializer))
1553 }
1554
1555 fn function(
1556 &mut self,
1557 f: &ast::Function<'source>,
1558 span: Span,
1559 ctx: &mut GlobalContext<'source, '_, '_>,
1560 ) -> Result<'source, LoweredGlobalDecl> {
1561 let mut local_table = FastHashMap::default();
1562 let mut expressions = Arena::new();
1563 let mut named_expressions = FastIndexMap::default();
1564 let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new();
1565
1566 let arguments = f
1567 .arguments
1568 .iter()
1569 .enumerate()
1570 .map(|(i, arg)| -> Result<'_, _> {
1571 let ty = self.resolve_ast_type(&arg.ty, &mut ctx.as_const())?;
1572 let expr =
1573 expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span);
1574 local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr)));
1575 named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));
1576 local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime);
1577
1578 Ok(ir::FunctionArgument {
1579 name: Some(arg.name.name.to_string()),
1580 ty,
1581 binding: self.binding(&arg.binding, ty, ctx)?,
1582 })
1583 })
1584 .collect::<Result<Vec<_>>>()?;
1585
1586 let result = f
1587 .result
1588 .as_ref()
1589 .map(|res| -> Result<'_, _> {
1590 let ty = self.resolve_ast_type(&res.ty, &mut ctx.as_const())?;
1591 Ok(ir::FunctionResult {
1592 ty,
1593 binding: self.binding(&res.binding, ty, ctx)?,
1594 })
1595 })
1596 .transpose()?;
1597
1598 let mut function = ir::Function {
1599 name: Some(f.name.name.to_string()),
1600 arguments,
1601 result,
1602 local_variables: Arena::new(),
1603 expressions,
1604 named_expressions: crate::NamedExpressions::default(),
1605 body: ir::Block::default(),
1606 diagnostic_filter_leaf: f.diagnostic_filter_leaf,
1607 };
1608
1609 let mut typifier = Typifier::default();
1610 let mut stmt_ctx = StatementContext {
1611 enable_extensions: ctx.enable_extensions,
1612 local_table: &mut local_table,
1613 globals: ctx.globals,
1614 ast_expressions: ctx.ast_expressions,
1615 const_typifier: ctx.const_typifier,
1616 typifier: &mut typifier,
1617 layouter: ctx.layouter,
1618 function: &mut function,
1619 named_expressions: &mut named_expressions,
1620 module: ctx.module,
1621 local_expression_kind_tracker: &mut local_expression_kind_tracker,
1622 global_expression_kind_tracker: ctx.global_expression_kind_tracker,
1623 };
1624 let mut body = self.block(&f.body, false, &mut stmt_ctx)?;
1625 proc::ensure_block_returns(&mut body);
1626
1627 function.body = body;
1628 function.named_expressions = named_expressions
1629 .into_iter()
1630 .map(|(key, (name, _))| (key, name))
1631 .collect();
1632
1633 if let Some(ref entry) = f.entry_point {
1634 let (workgroup_size, workgroup_size_overrides) =
1635 if let Some(workgroup_size) = entry.workgroup_size {
1636 let mut workgroup_size_out = [1; 3];
1638 let mut workgroup_size_overrides_out = [None; 3];
1639 for (i, size) in workgroup_size.into_iter().enumerate() {
1640 if let Some(size_expr) = size {
1641 match self.const_u32(size_expr, &mut ctx.as_const()) {
1642 Ok(value) => {
1643 workgroup_size_out[i] = value.0;
1644 }
1645 Err(err) => {
1646 if let Error::ConstantEvaluatorError(ref ty, _) = *err {
1647 match **ty {
1648 proc::ConstantEvaluatorError::OverrideExpr => {
1649 workgroup_size_overrides_out[i] =
1650 Some(self.workgroup_size_override(
1651 size_expr,
1652 &mut ctx.as_override(),
1653 )?);
1654 }
1655 _ => {
1656 return Err(err);
1657 }
1658 }
1659 } else {
1660 return Err(err);
1661 }
1662 }
1663 }
1664 }
1665 }
1666 if workgroup_size_overrides_out.iter().all(|x| x.is_none()) {
1667 (workgroup_size_out, None)
1668 } else {
1669 (workgroup_size_out, Some(workgroup_size_overrides_out))
1670 }
1671 } else {
1672 ([0; 3], None)
1673 };
1674
1675 let mesh_info = if let Some((var_name, var_span)) = entry.mesh_output_variable {
1676 let var = match ctx.globals.get(var_name) {
1677 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1678 Some(_) => {
1679 return Err(Box::new(Error::ExpectedGlobalVariable {
1680 name_span: var_span,
1681 }))
1682 }
1683 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1684 };
1685
1686 let mut info = ctx.module.analyze_mesh_shader_info(var);
1687 if let Some(h) = info.1[0] {
1688 info.0.max_vertices_override = Some(
1689 ctx.module
1690 .global_expressions
1691 .append(crate::Expression::Override(h), Span::UNDEFINED),
1692 );
1693 }
1694 if let Some(h) = info.1[1] {
1695 info.0.max_primitives_override = Some(
1696 ctx.module
1697 .global_expressions
1698 .append(crate::Expression::Override(h), Span::UNDEFINED),
1699 );
1700 }
1701
1702 Some(info.0)
1703 } else {
1704 None
1705 };
1706
1707 let task_payload = if let Some((var_name, var_span)) = entry.task_payload {
1708 Some(match ctx.globals.get(var_name) {
1709 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1710 Some(_) => {
1711 return Err(Box::new(Error::ExpectedGlobalVariable {
1712 name_span: var_span,
1713 }))
1714 }
1715 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1716 })
1717 } else {
1718 None
1719 };
1720
1721 let incoming_ray_payload =
1722 if let Some((var_name, var_span)) = entry.ray_incoming_payload {
1723 Some(match ctx.globals.get(var_name) {
1724 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1725 Some(_) => {
1726 return Err(Box::new(Error::ExpectedGlobalVariable {
1727 name_span: var_span,
1728 }))
1729 }
1730 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1731 })
1732 } else {
1733 None
1734 };
1735
1736 ctx.module.entry_points.push(ir::EntryPoint {
1737 name: f.name.name.to_string(),
1738 stage: entry.stage,
1739 early_depth_test: entry.early_depth_test,
1740 workgroup_size,
1741 workgroup_size_overrides,
1742 function,
1743 mesh_info,
1744 task_payload,
1745 incoming_ray_payload,
1746 });
1747 Ok(LoweredGlobalDecl::EntryPoint(
1748 ctx.module.entry_points.len() - 1,
1749 ))
1750 } else {
1751 let handle = ctx.module.functions.append(function, span);
1752 Ok(LoweredGlobalDecl::Function {
1753 handle,
1754 must_use: f.result.as_ref().is_some_and(|res| res.must_use),
1755 })
1756 }
1757 }
1758
1759 fn workgroup_size_override(
1760 &mut self,
1761 size_expr: Handle<ast::Expression<'source>>,
1762 ctx: &mut ExpressionContext<'source, '_, '_>,
1763 ) -> Result<'source, Handle<ir::Expression>> {
1764 let span = ctx.ast_expressions.get_span(size_expr);
1765 let expr = self.expression(size_expr, ctx)?;
1766 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
1767 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr),
1768 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
1769 span,
1770 ))),
1771 }
1772 }
1773
1774 fn block(
1775 &mut self,
1776 b: &ast::Block<'source>,
1777 is_inside_loop: bool,
1778 ctx: &mut StatementContext<'source, '_, '_>,
1779 ) -> Result<'source, ir::Block> {
1780 let mut block = ir::Block::default();
1781
1782 for stmt in b.stmts.iter() {
1783 self.statement(stmt, &mut block, is_inside_loop, ctx)?;
1784 }
1785
1786 Ok(block)
1787 }
1788
1789 fn statement(
1790 &mut self,
1791 stmt: &ast::Statement<'source>,
1792 block: &mut ir::Block,
1793 is_inside_loop: bool,
1794 ctx: &mut StatementContext<'source, '_, '_>,
1795 ) -> Result<'source, ()> {
1796 let out = match stmt.kind {
1797 ast::StatementKind::Block(ref block) => {
1798 let block = self.block(block, is_inside_loop, ctx)?;
1799 ir::Statement::Block(block)
1800 }
1801 ast::StatementKind::LocalDecl(ref decl) => match *decl {
1802 ast::LocalDecl::Let(ref l) => {
1803 let mut emitter = proc::Emitter::default();
1804 emitter.start(&ctx.function.expressions);
1805
1806 let explicit_ty = l
1807 .ty
1808 .as_ref()
1809 .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter)))
1810 .transpose()?;
1811
1812 let mut ectx = ctx.as_expression(block, &mut emitter);
1813
1814 let (ty, initializer) = self.type_and_init(
1815 l.name,
1816 Some(l.init),
1817 explicit_ty,
1818 AbstractRule::Concretize,
1819 &mut ectx,
1820 )?;
1821
1822 if ctx.module.types[ty]
1830 .inner
1831 .is_dynamically_sized(&ctx.module.types)
1832 {
1833 return Err(Box::new(Error::TypeNotConstructible(l.name.span)));
1834 }
1835
1836 let initializer =
1839 initializer.expect("type_and_init did not return an initializer");
1840
1841 ctx.local_expression_kind_tracker
1847 .force_non_const(initializer);
1848
1849 block.extend(emitter.finish(&ctx.function.expressions));
1850 ctx.local_table
1851 .insert(l.handle, Declared::Runtime(Typed::Plain(initializer)));
1852 ctx.named_expressions
1853 .insert(initializer, (l.name.name.to_string(), l.name.span));
1854
1855 return Ok(());
1856 }
1857 ast::LocalDecl::Var(ref v) => {
1858 let mut emitter = proc::Emitter::default();
1859 emitter.start(&ctx.function.expressions);
1860
1861 let explicit_ty =
1862 v.ty.as_ref()
1863 .map(|ast| {
1864 self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter))
1865 })
1866 .transpose()?;
1867
1868 let mut ectx = ctx.as_expression(block, &mut emitter);
1869 let (ty, initializer) = self.type_and_init(
1870 v.name,
1871 v.init,
1872 explicit_ty,
1873 AbstractRule::Concretize,
1874 &mut ectx,
1875 )?;
1876
1877 let (const_initializer, initializer) = {
1878 match initializer {
1879 Some(init) => {
1880 if is_inside_loop
1888 || !ctx.local_expression_kind_tracker.is_const_or_override(init)
1889 {
1890 (None, Some(init))
1891 } else {
1892 (Some(init), None)
1893 }
1894 }
1895 None => (None, None),
1896 }
1897 };
1898
1899 let var = ctx.function.local_variables.append(
1900 ir::LocalVariable {
1901 name: Some(v.name.name.to_string()),
1902 ty,
1903 init: const_initializer,
1904 },
1905 stmt.span,
1906 );
1907
1908 let handle = ctx
1909 .as_expression(block, &mut emitter)
1910 .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?;
1911 block.extend(emitter.finish(&ctx.function.expressions));
1912 ctx.local_table
1913 .insert(v.handle, Declared::Runtime(Typed::Reference(handle)));
1914
1915 match initializer {
1916 Some(initializer) => ir::Statement::Store {
1917 pointer: handle,
1918 value: initializer,
1919 },
1920 None => return Ok(()),
1921 }
1922 }
1923 ast::LocalDecl::Const(ref c) => {
1924 let mut emitter = proc::Emitter::default();
1925 emitter.start(&ctx.function.expressions);
1926
1927 let ectx = &mut ctx.as_const(block, &mut emitter);
1928
1929 let explicit_ty =
1930 c.ty.as_ref()
1931 .map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const()))
1932 .transpose()?;
1933
1934 let (_ty, init) = self.type_and_init(
1935 c.name,
1936 Some(c.init),
1937 explicit_ty,
1938 AbstractRule::Allow,
1939 &mut ectx.as_const(),
1940 )?;
1941 let init = init.expect("Local const must have init");
1942
1943 block.extend(emitter.finish(&ctx.function.expressions));
1944 ctx.local_table
1945 .insert(c.handle, Declared::Const(Typed::Plain(init)));
1946 return Ok(());
1947 }
1948 },
1949 ast::StatementKind::If {
1950 condition,
1951 ref accept,
1952 ref reject,
1953 } => {
1954 let mut emitter = proc::Emitter::default();
1955 emitter.start(&ctx.function.expressions);
1956
1957 let condition =
1958 self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;
1959 block.extend(emitter.finish(&ctx.function.expressions));
1960
1961 let accept = self.block(accept, is_inside_loop, ctx)?;
1962 let reject = self.block(reject, is_inside_loop, ctx)?;
1963
1964 ir::Statement::If {
1965 condition,
1966 accept,
1967 reject,
1968 }
1969 }
1970 ast::StatementKind::Switch {
1971 selector,
1972 ref cases,
1973 } => {
1974 let mut emitter = proc::Emitter::default();
1975 emitter.start(&ctx.function.expressions);
1976
1977 let mut ectx = ctx.as_expression(block, &mut emitter);
1978
1979 let (mut exprs, spans) = core::iter::once(selector)
1982 .chain(cases.iter().filter_map(|case| match case.value {
1983 ast::SwitchValue::Expr(expr) => Some(expr),
1984 ast::SwitchValue::Default => None,
1985 }))
1986 .enumerate()
1987 .map(|(i, expr)| {
1988 let span = ectx.ast_expressions.get_span(expr);
1989 let expr = self.expression_for_abstract(expr, &mut ectx)?;
1990 let ty = resolve_inner!(ectx, expr);
1991 match *ty {
1992 ir::TypeInner::Scalar(
1993 ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT,
1994 ) => Ok((expr, span)),
1995 _ => match i {
1996 0 => Err(Box::new(Error::InvalidSwitchSelector { span })),
1997 _ => Err(Box::new(Error::InvalidSwitchCase { span })),
1998 },
1999 }
2000 })
2001 .collect::<Result<(Vec<_>, Vec<_>)>>()?;
2002
2003 let mut consensus =
2004 ectx.automatic_conversion_consensus(None, &exprs)
2005 .map_err(|span_idx| Error::SwitchCaseTypeMismatch {
2006 span: spans[span_idx],
2007 })?;
2008 if consensus == ir::Scalar::ABSTRACT_INT {
2010 consensus = ir::Scalar::I32;
2011 }
2012 for expr in &mut exprs {
2013 ectx.convert_to_leaf_scalar(expr, consensus)?;
2014 }
2015
2016 block.extend(emitter.finish(&ctx.function.expressions));
2017
2018 let mut exprs = exprs.into_iter();
2019 let selector = exprs
2020 .next()
2021 .expect("First element should be selector expression");
2022
2023 let cases = cases
2024 .iter()
2025 .map(|case| {
2026 Ok(ir::SwitchCase {
2027 value: match case.value {
2028 ast::SwitchValue::Expr(expr) => {
2029 let span = ctx.ast_expressions.get_span(expr);
2030 let expr = exprs.next().expect(
2031 "Should yield expression for each SwitchValue::Expr case",
2032 );
2033 match ctx
2034 .module
2035 .to_ctx()
2036 .get_const_val_from(expr, &ctx.function.expressions)
2037 {
2038 Ok(ir::Literal::I32(value)) => ir::SwitchValue::I32(value),
2039 Ok(ir::Literal::U32(value)) => ir::SwitchValue::U32(value),
2040 _ => {
2041 return Err(Box::new(Error::InvalidSwitchCase {
2042 span,
2043 }));
2044 }
2045 }
2046 }
2047 ast::SwitchValue::Default => ir::SwitchValue::Default,
2048 },
2049 body: self.block(&case.body, is_inside_loop, ctx)?,
2050 fall_through: case.fall_through,
2051 })
2052 })
2053 .collect::<Result<_>>()?;
2054
2055 ir::Statement::Switch { selector, cases }
2056 }
2057 ast::StatementKind::Loop {
2058 ref body,
2059 ref continuing,
2060 break_if,
2061 } => {
2062 let body = self.block(body, true, ctx)?;
2063 let mut continuing = self.block(continuing, true, ctx)?;
2064
2065 let mut emitter = proc::Emitter::default();
2066 emitter.start(&ctx.function.expressions);
2067 let break_if = break_if
2068 .map(|expr| {
2069 self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))
2070 })
2071 .transpose()?;
2072 continuing.extend(emitter.finish(&ctx.function.expressions));
2073
2074 ir::Statement::Loop {
2075 body,
2076 continuing,
2077 break_if,
2078 }
2079 }
2080 ast::StatementKind::Break => ir::Statement::Break,
2081 ast::StatementKind::Continue => ir::Statement::Continue,
2082 ast::StatementKind::Return { value: ast_value } => {
2083 let mut emitter = proc::Emitter::default();
2084 emitter.start(&ctx.function.expressions);
2085
2086 let value;
2087 if let Some(ast_expr) = ast_value {
2088 let result_ty = ctx.function.result.as_ref().map(|r| r.ty);
2089 let mut ectx = ctx.as_expression(block, &mut emitter);
2090 let expr = self.expression_for_abstract(ast_expr, &mut ectx)?;
2091
2092 if let Some(result_ty) = result_ty {
2093 let mut ectx = ctx.as_expression(block, &mut emitter);
2094 let resolution = proc::TypeResolution::Handle(result_ty);
2095 let converted =
2096 ectx.try_automatic_conversions(expr, &resolution, Span::default())?;
2097 value = Some(converted);
2098 } else {
2099 value = Some(expr);
2100 }
2101 } else {
2102 value = None;
2103 }
2104 block.extend(emitter.finish(&ctx.function.expressions));
2105
2106 ir::Statement::Return { value }
2107 }
2108 ast::StatementKind::Kill => ir::Statement::Kill,
2109 ast::StatementKind::Call(ref call_phrase) => {
2110 let mut emitter = proc::Emitter::default();
2111 emitter.start(&ctx.function.expressions);
2112
2113 let _ = self.call(
2114 call_phrase,
2115 stmt.span,
2116 &mut ctx.as_expression(block, &mut emitter),
2117 true,
2118 )?;
2119 block.extend(emitter.finish(&ctx.function.expressions));
2120 return Ok(());
2121 }
2122 ast::StatementKind::Assign {
2123 target: ast_target,
2124 op,
2125 value,
2126 } => {
2127 let mut emitter = proc::Emitter::default();
2128 emitter.start(&ctx.function.expressions);
2129 let target_span = ctx.ast_expressions.get_span(ast_target);
2130
2131 let mut ectx = ctx.as_expression(block, &mut emitter);
2132 let target = self.expression_for_reference(ast_target, &mut ectx)?;
2133 let target_handle = match target {
2134 Typed::Reference(handle) => handle,
2135 Typed::Plain(handle) => {
2136 let ty = ctx.invalid_assignment_type(handle);
2137 return Err(Box::new(Error::InvalidAssignment {
2138 span: target_span,
2139 ty,
2140 }));
2141 }
2142 };
2143
2144 let target_scalar = match op {
2149 Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => {
2150 Some(ir::Scalar::U32)
2151 }
2152 _ => resolve_inner!(ectx, target_handle)
2153 .pointer_automatically_convertible_scalar(&ectx.module.types),
2154 };
2155
2156 let value = self.expression_for_abstract(value, &mut ectx)?;
2157 let mut value = match target_scalar {
2158 Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar(
2159 value,
2160 target_scalar,
2161 target_span,
2162 )?,
2163 None => value,
2164 };
2165
2166 let value = match op {
2167 Some(op) => {
2168 let mut left = ectx.apply_load_rule(target)?;
2169 ectx.binary_op_splat(op, &mut left, &mut value)?;
2170 ectx.append_expression(
2171 ir::Expression::Binary {
2172 op,
2173 left,
2174 right: value,
2175 },
2176 stmt.span,
2177 )?
2178 }
2179 None => value,
2180 };
2181 block.extend(emitter.finish(&ctx.function.expressions));
2182
2183 ir::Statement::Store {
2184 pointer: target_handle,
2185 value,
2186 }
2187 }
2188 ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {
2189 let mut emitter = proc::Emitter::default();
2190 emitter.start(&ctx.function.expressions);
2191
2192 let op = match stmt.kind {
2193 ast::StatementKind::Increment(_) => ir::BinaryOperator::Add,
2194 ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract,
2195 _ => unreachable!(),
2196 };
2197
2198 let value_span = ctx.ast_expressions.get_span(value);
2199 let target = self
2200 .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;
2201 let target_handle = target.ref_or(Error::BadIncrDecrReferenceType(value_span))?;
2202
2203 let mut ectx = ctx.as_expression(block, &mut emitter);
2204 let scalar = match *resolve_inner!(ectx, target_handle) {
2205 ir::TypeInner::ValuePointer {
2206 size: None, scalar, ..
2207 } => scalar,
2208 ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {
2209 ir::TypeInner::Scalar(scalar) => scalar,
2210 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2211 },
2212 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2213 };
2214 let literal = match scalar.kind {
2215 ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar)
2216 .ok_or(Error::BadIncrDecrReferenceType(value_span))?,
2217 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2218 };
2219
2220 let right =
2221 ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?;
2222 let rctx = ectx.runtime_expression_ctx(stmt.span)?;
2223 let left = rctx.function.expressions.append(
2224 ir::Expression::Load {
2225 pointer: target_handle,
2226 },
2227 value_span,
2228 );
2229 let value = rctx
2230 .function
2231 .expressions
2232 .append(ir::Expression::Binary { op, left, right }, stmt.span);
2233 rctx.local_expression_kind_tracker
2234 .insert(left, proc::ExpressionKind::Runtime);
2235 rctx.local_expression_kind_tracker
2236 .insert(value, proc::ExpressionKind::Runtime);
2237
2238 block.extend(emitter.finish(&ctx.function.expressions));
2239 ir::Statement::Store {
2240 pointer: target_handle,
2241 value,
2242 }
2243 }
2244 ast::StatementKind::ConstAssert(condition) => {
2245 let mut emitter = proc::Emitter::default();
2246 emitter.start(&ctx.function.expressions);
2247
2248 let condition =
2249 self.expression(condition, &mut ctx.as_const(block, &mut emitter))?;
2250
2251 let span = ctx.function.expressions.get_span(condition);
2252 match ctx
2253 .module
2254 .to_ctx()
2255 .get_const_val_from(condition, &ctx.function.expressions)
2256 {
2257 Ok(true) => Ok(()),
2258 Ok(false) => Err(Error::ConstAssertFailed(span)),
2259 Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
2260 unreachable!()
2261 }
2262 Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
2263 }?;
2264
2265 block.extend(emitter.finish(&ctx.function.expressions));
2266
2267 return Ok(());
2268 }
2269 ast::StatementKind::Phony(expr) => {
2270 let mut emitter = proc::Emitter::default();
2274 emitter.start(&ctx.function.expressions);
2275
2276 let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;
2277 block.extend(emitter.finish(&ctx.function.expressions));
2278 ctx.named_expressions
2279 .insert(value, ("phony".to_string(), stmt.span));
2280 return Ok(());
2281 }
2282 };
2283
2284 block.push(out, stmt.span);
2285
2286 Ok(())
2287 }
2288
2289 fn expression(
2297 &mut self,
2298 expr: Handle<ast::Expression<'source>>,
2299 ctx: &mut ExpressionContext<'source, '_, '_>,
2300 ) -> Result<'source, Handle<ir::Expression>> {
2301 let expr = self.expression_for_abstract(expr, ctx)?;
2302 ctx.concretize(expr)
2303 }
2304
2305 fn expression_for_abstract(
2306 &mut self,
2307 expr: Handle<ast::Expression<'source>>,
2308 ctx: &mut ExpressionContext<'source, '_, '_>,
2309 ) -> Result<'source, Handle<ir::Expression>> {
2310 let expr = self.expression_for_reference(expr, ctx)?;
2311 ctx.apply_load_rule(expr)
2312 }
2313
2314 fn expression_with_leaf_scalar(
2315 &mut self,
2316 expr: Handle<ast::Expression<'source>>,
2317 scalar: ir::Scalar,
2318 ctx: &mut ExpressionContext<'source, '_, '_>,
2319 ) -> Result<'source, Handle<ir::Expression>> {
2320 let unconverted = self.expression_for_abstract(expr, ctx)?;
2321 ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default())
2322 }
2323
2324 fn expression_for_reference(
2325 &mut self,
2326 expr: Handle<ast::Expression<'source>>,
2327 ctx: &mut ExpressionContext<'source, '_, '_>,
2328 ) -> Result<'source, Typed<Handle<ir::Expression>>> {
2329 let span = ctx.ast_expressions.get_span(expr);
2330 let expr = &ctx.ast_expressions[expr];
2331
2332 let expr: Typed<ir::Expression> = match *expr {
2333 ast::Expression::Literal(literal) => {
2334 let literal = match literal {
2335 ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f),
2336 ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f),
2337 ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i),
2338 ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u),
2339 ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i),
2340 ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u),
2341 ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f),
2342 ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i),
2343 ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f),
2344 ast::Literal::Bool(b) => ir::Literal::Bool(b),
2345 };
2346 let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?;
2347 return Ok(Typed::Plain(handle));
2348 }
2349 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2350 ref template_list, ..
2351 }) if !template_list.is_empty() => {
2352 return Err(Box::new(Error::UnexpectedTemplate(span)))
2353 }
2354 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2355 ident: ast::IdentExpr::Local(local),
2356 ..
2357 }) => {
2358 return ctx.local(&local, span);
2359 }
2360 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2361 ident: ast::IdentExpr::Unresolved(name),
2362 ..
2363 }) => {
2364 let global = ctx
2365 .globals
2366 .get(name)
2367 .ok_or(Error::UnknownIdent(span, name))?;
2368 let expr = match *global {
2369 LoweredGlobalDecl::Var(handle) => {
2370 let expr = ir::Expression::GlobalVariable(handle);
2371 let v = &ctx.module.global_variables[handle];
2372 match v.space {
2373 ir::AddressSpace::Handle => Typed::Plain(expr),
2374 _ => Typed::Reference(expr),
2375 }
2376 }
2377 LoweredGlobalDecl::Const(handle) => {
2378 Typed::Plain(ir::Expression::Constant(handle))
2379 }
2380 LoweredGlobalDecl::Override(handle) => {
2381 Typed::Plain(ir::Expression::Override(handle))
2382 }
2383 LoweredGlobalDecl::Function { .. }
2384 | LoweredGlobalDecl::Type(_)
2385 | LoweredGlobalDecl::EntryPoint(_) => {
2386 return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable)));
2387 }
2388 };
2389
2390 return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));
2391 }
2392 ast::Expression::Unary { op, expr } => {
2393 let expr = self.expression_for_abstract(expr, ctx)?;
2394 Typed::Plain(ir::Expression::Unary { op, expr })
2395 }
2396 ast::Expression::AddrOf(expr) => {
2397 match self.expression_for_reference(expr, ctx)? {
2400 Typed::Reference(handle) => {
2401 let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle];
2402 if let &ir::Expression::Access { base, .. }
2403 | &ir::Expression::AccessIndex { base, .. } = expr
2404 {
2405 if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() {
2406 if matches!(
2407 *ty.inner_with(&ctx.module.types),
2408 ir::TypeInner::Vector { .. },
2409 ) {
2410 return Err(Box::new(Error::InvalidAddrOfOperand(
2411 ctx.get_expression_span(handle),
2412 )));
2413 }
2414 }
2415 }
2416 return Ok(Typed::Plain(handle));
2418 }
2419 Typed::Plain(_) => {
2420 return Err(Box::new(Error::NotReference(
2421 "the operand of the `&` operator",
2422 span,
2423 )));
2424 }
2425 }
2426 }
2427 ast::Expression::Deref(expr) => {
2428 let pointer = self.expression(expr, ctx)?;
2430
2431 if resolve_inner!(ctx, pointer).pointer_space().is_none() {
2432 return Err(Box::new(Error::NotPointer(span)));
2433 }
2434
2435 return Ok(Typed::Reference(pointer));
2437 }
2438 ast::Expression::Binary { op, left, right } => {
2439 self.binary(op, left, right, span, ctx)?
2440 }
2441 ast::Expression::Call(ref call_phrase) => {
2442 let handle = self
2443 .call(call_phrase, span, ctx, false)?
2444 .ok_or(Error::FunctionReturnsVoid(span))?;
2445 return Ok(Typed::Plain(handle));
2446 }
2447 ast::Expression::Index { base, index } => {
2448 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2449 let index = self.expression(index, ctx)?;
2450
2451 if let Typed::Plain(handle) = lowered_base {
2454 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2455 lowered_base = Typed::Reference(handle);
2456 }
2457 }
2458
2459 lowered_base.try_map(|base| match ctx.get_const_val(index).ok() {
2460 Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }),
2461 None => {
2462 let base = ctx.concretize(base)?;
2468 Ok(ir::Expression::Access { base, index })
2469 }
2470 })?
2471 }
2472 ast::Expression::Member { base, ref field } => {
2473 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2474
2475 if let Typed::Plain(handle) = lowered_base {
2478 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2479 lowered_base = Typed::Reference(handle);
2480 }
2481 }
2482
2483 let temp_ty;
2484 let composite_type: &ir::TypeInner = match lowered_base {
2485 Typed::Reference(handle) => {
2486 temp_ty = resolve_inner!(ctx, handle)
2487 .pointer_base_type()
2488 .expect("In Typed::Reference(handle), handle must be a Naga pointer");
2489 temp_ty.inner_with(&ctx.module.types)
2490 }
2491
2492 Typed::Plain(handle) => {
2493 resolve_inner!(ctx, handle)
2494 }
2495 };
2496
2497 let access = match *composite_type {
2498 ir::TypeInner::Struct { ref members, .. } => {
2499 let index = members
2500 .iter()
2501 .position(|m| m.name.as_deref() == Some(field.name))
2502 .ok_or(Error::BadAccessor(field.span))?
2503 as u32;
2504
2505 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2506 }
2507 ir::TypeInner::Vector { size: vec_size, .. } => {
2508 match Components::new(field.name, field.span)? {
2509 Components::Swizzle { size, pattern } => {
2510 for &component in pattern[..size as usize].iter() {
2511 if component as u8 >= vec_size as u8 {
2512 return Err(Box::new(Error::BadAccessor(field.span)));
2513 }
2514 }
2515 Typed::Plain(ir::Expression::Swizzle {
2516 size,
2517 vector: ctx.apply_load_rule(lowered_base)?,
2518 pattern,
2519 })
2520 }
2521 Components::Single(index) => {
2522 if index >= vec_size as u32 {
2523 return Err(Box::new(Error::BadAccessor(field.span)));
2524 }
2525 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2526 }
2527 }
2528 }
2529 _ => return Err(Box::new(Error::BadAccessor(field.span))),
2530 };
2531
2532 access
2533 }
2534 };
2535
2536 expr.try_map(|handle| ctx.append_expression(handle, span))
2537 }
2538
2539 fn logical(
2543 &mut self,
2544 op: crate::BinaryOperator,
2545 left: Handle<crate::Expression>,
2546 right: Handle<ast::Expression<'source>>,
2547 span: Span,
2548 ctx: &mut ExpressionContext<'source, '_, '_>,
2549 ) -> Result<'source, Typed<crate::Expression>> {
2550 debug_assert!(
2551 op == crate::BinaryOperator::LogicalAnd || op == crate::BinaryOperator::LogicalOr
2552 );
2553
2554 if ctx.is_runtime() {
2555 let (condition, else_val) = if op == crate::BinaryOperator::LogicalAnd {
2567 let condition = left;
2568 let else_val = ctx.append_expression(
2569 crate::Expression::Literal(crate::Literal::Bool(false)),
2570 span,
2571 )?;
2572 (condition, else_val)
2573 } else {
2574 let condition = ctx.append_expression(
2575 crate::Expression::Unary {
2576 op: crate::UnaryOperator::LogicalNot,
2577 expr: left,
2578 },
2579 span,
2580 )?;
2581 let else_val = ctx.append_expression(
2582 crate::Expression::Literal(crate::Literal::Bool(true)),
2583 span,
2584 )?;
2585 (condition, else_val)
2586 };
2587
2588 let bool_ty = ctx.ensure_type_exists(crate::TypeInner::Scalar(crate::Scalar::BOOL));
2589
2590 let rctx = ctx.runtime_expression_ctx(span)?;
2591 let result_var = rctx.function.local_variables.append(
2592 crate::LocalVariable {
2593 name: None,
2594 ty: bool_ty,
2595 init: None,
2596 },
2597 span,
2598 );
2599 let pointer =
2600 ctx.append_expression(crate::Expression::LocalVariable(result_var), span)?;
2601
2602 let (right, mut accept) = ctx.with_nested_runtime_expression_ctx(span, |ctx| {
2603 let right = self.expression_for_abstract(right, ctx)?;
2604 ctx.grow_types(right)?;
2605 Ok(right)
2606 })?;
2607
2608 accept.push(
2609 crate::Statement::Store {
2610 pointer,
2611 value: right,
2612 },
2613 span,
2614 );
2615
2616 let mut reject = crate::Block::with_capacity(1);
2617 reject.push(
2618 crate::Statement::Store {
2619 pointer,
2620 value: else_val,
2621 },
2622 span,
2623 );
2624
2625 let rctx = ctx.runtime_expression_ctx(span)?;
2626 rctx.block.push(
2627 crate::Statement::If {
2628 condition,
2629 accept,
2630 reject,
2631 },
2632 span,
2633 );
2634
2635 Ok(Typed::Reference(crate::Expression::LocalVariable(
2636 result_var,
2637 )))
2638 } else {
2639 let left_val: Option<bool> = ctx.get_const_val(left).ok();
2640
2641 if left_val.is_some_and(|left_val| {
2642 op == crate::BinaryOperator::LogicalAnd && !left_val
2643 || op == crate::BinaryOperator::LogicalOr && left_val
2644 }) {
2645 Ok(Typed::Plain(ctx.get(left).clone()))
2654 } else {
2655 let right = self.expression_for_abstract(right, ctx)?;
2661 ctx.grow_types(right)?;
2662
2663 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2664 }
2665 }
2666 }
2667
2668 fn type_expression(
2669 &mut self,
2670 expr: Handle<ast::Expression<'source>>,
2671 ctx: &mut ExpressionContext<'source, '_, '_>,
2672 ) -> Result<'source, Handle<ir::Type>> {
2673 let span = ctx.ast_expressions.get_span(expr);
2674 let expr = &ctx.ast_expressions[expr];
2675
2676 let ident = match *expr {
2677 ast::Expression::Ident(ref ident) => ident,
2678 _ => return Err(Box::new(Error::UnexpectedExprForTypeExpression(span))),
2679 };
2680
2681 self.type_specifier(ident, ctx, None)
2682 }
2683
2684 fn type_specifier(
2685 &mut self,
2686 ident: &ast::TemplateElaboratedIdent<'source>,
2687 ctx: &mut ExpressionContext<'source, '_, '_>,
2688 alias_name: Option<String>,
2689 ) -> Result<'source, Handle<ir::Type>> {
2690 let &ast::TemplateElaboratedIdent {
2691 ref ident,
2692 ident_span,
2693 ref template_list,
2694 ..
2695 } = ident;
2696
2697 let ident = match *ident {
2698 ast::IdentExpr::Unresolved(ident) => ident,
2699 ast::IdentExpr::Local(_) => {
2700 return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2703 }
2704 };
2705
2706 let mut tl = TemplateListIter::new(ident_span, template_list);
2707
2708 if let Some(global) = ctx.globals.get(ident) {
2709 let &LoweredGlobalDecl::Type(handle) = global else {
2710 return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2711 };
2712
2713 tl.finish(ctx)?;
2717 return Ok(handle);
2718 }
2719
2720 let ty = conv::map_predeclared_type(&ctx.enable_extensions, ident_span, ident)?
2723 .ok_or_else(|| Box::new(Error::UnknownIdent(ident_span, ident)))?;
2724 let ty = self.finalize_type(ctx, ty, &mut tl, alias_name)?;
2725
2726 tl.finish(ctx)?;
2727
2728 Ok(ty)
2729 }
2730
2731 fn finalize_type(
2749 &mut self,
2750 ctx: &mut ExpressionContext<'source, '_, '_>,
2751 ty: conv::PredeclaredType,
2752 tl: &mut TemplateListIter<'_, 'source>,
2753 alias_name: Option<String>,
2754 ) -> Result<'source, Handle<ir::Type>> {
2755 let ty = match ty {
2756 conv::PredeclaredType::TypeInner(ty_inner) => {
2757 if let ir::TypeInner::Image {
2758 class: ir::ImageClass::External,
2759 ..
2760 } = ty_inner
2761 {
2762 ctx.module.generate_external_texture_types();
2777 }
2778
2779 ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2780 }
2781 conv::PredeclaredType::RayDesc => ctx.module.generate_ray_desc_type(),
2782 conv::PredeclaredType::RayIntersection => ctx.module.generate_ray_intersection_type(),
2783 conv::PredeclaredType::TypeGenerator(type_generator) => {
2784 let ty_inner = match type_generator {
2785 conv::TypeGenerator::Vector { size } => {
2786 let (scalar, _) = tl.scalar_ty(self, ctx)?;
2787 ir::TypeInner::Vector { size, scalar }
2788 }
2789 conv::TypeGenerator::Matrix { columns, rows } => {
2790 let (scalar, span) = tl.scalar_ty(self, ctx)?;
2791 if scalar.kind != ir::ScalarKind::Float {
2792 return Err(Box::new(Error::BadMatrixScalarKind(span, scalar)));
2793 }
2794 ir::TypeInner::Matrix {
2795 columns,
2796 rows,
2797 scalar,
2798 }
2799 }
2800 conv::TypeGenerator::Array => {
2801 let base = tl.ty(self, ctx)?;
2802 let size = tl.maybe_array_size(self, ctx)?;
2803
2804 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
2806 let LayoutErrorInner::TooLarge = err.inner else {
2807 unreachable!("unexpected layout error: {err:?}");
2808 };
2809 Box::new(Error::TypeTooLarge {
2812 span: ctx.module.types.get_span(err.ty),
2813 })
2814 })?;
2815 let stride = ctx.layouter[base].to_stride();
2816
2817 ir::TypeInner::Array { base, size, stride }
2818 }
2819 conv::TypeGenerator::Atomic => {
2820 let (scalar, _) = tl.scalar_ty(self, ctx)?;
2821 ir::TypeInner::Atomic(scalar)
2822 }
2823 conv::TypeGenerator::Pointer => {
2824 let mut space = tl.address_space(ctx)?;
2825 let base = tl.ty(self, ctx)?;
2826 tl.maybe_access_mode(&mut space, ctx)?;
2827 ir::TypeInner::Pointer { base, space }
2828 }
2829 conv::TypeGenerator::SampledTexture {
2830 dim,
2831 arrayed,
2832 multi,
2833 } => {
2834 let (scalar, span) = tl.scalar_ty(self, ctx)?;
2835 let ir::Scalar { kind, width } = scalar;
2836 if width != 4 {
2837 return Err(Box::new(Error::BadTextureSampleType { span, scalar }));
2838 }
2839 ir::TypeInner::Image {
2840 dim,
2841 arrayed,
2842 class: ir::ImageClass::Sampled { kind, multi },
2843 }
2844 }
2845 conv::TypeGenerator::StorageTexture { dim, arrayed } => {
2846 let format = tl.storage_format(ctx)?;
2847 let access = tl.access_mode(ctx)?;
2848 ir::TypeInner::Image {
2849 dim,
2850 arrayed,
2851 class: ir::ImageClass::Storage { format, access },
2852 }
2853 }
2854 conv::TypeGenerator::BindingArray => {
2855 let base = tl.ty(self, ctx)?;
2856 let size = tl.maybe_array_size(self, ctx)?;
2857 ir::TypeInner::BindingArray { base, size }
2858 }
2859 conv::TypeGenerator::AccelerationStructure => {
2860 let vertex_return = tl.maybe_vertex_return(ctx)?;
2861 ir::TypeInner::AccelerationStructure { vertex_return }
2862 }
2863 conv::TypeGenerator::RayQuery => {
2864 let vertex_return = tl.maybe_vertex_return(ctx)?;
2865 ir::TypeInner::RayQuery { vertex_return }
2866 }
2867 conv::TypeGenerator::CooperativeMatrix { columns, rows } => {
2868 let (ty, span) = tl.ty_with_span(self, ctx)?;
2869 let ir::TypeInner::Scalar(scalar) = ctx.module.types[ty].inner else {
2870 return Err(Box::new(Error::UnsupportedCooperativeScalar(span)));
2871 };
2872 let role = tl.cooperative_role(ctx)?;
2873 ir::TypeInner::CooperativeMatrix {
2874 columns,
2875 rows,
2876 scalar,
2877 role,
2878 }
2879 }
2880 };
2881 ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2882 }
2883 };
2884 Ok(ty)
2885 }
2886
2887 fn binary(
2888 &mut self,
2889 op: ir::BinaryOperator,
2890 left: Handle<ast::Expression<'source>>,
2891 right: Handle<ast::Expression<'source>>,
2892 span: Span,
2893 ctx: &mut ExpressionContext<'source, '_, '_>,
2894 ) -> Result<'source, Typed<ir::Expression>> {
2895 if op == ir::BinaryOperator::LogicalAnd || op == ir::BinaryOperator::LogicalOr {
2896 let left = self.expression_for_abstract(left, ctx)?;
2897 ctx.grow_types(left)?;
2898
2899 if !matches!(
2900 resolve_inner!(ctx, left),
2901 &ir::TypeInner::Scalar(ir::Scalar::BOOL)
2902 ) {
2903 let right = self.expression_for_abstract(right, ctx)?;
2905 ctx.grow_types(right)?;
2906 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2907 } else {
2908 self.logical(op, left, right, span, ctx)
2909 }
2910 } else {
2911 let mut left = self.expression_for_abstract(left, ctx)?;
2913 let mut right = self.expression_for_abstract(right, ctx)?;
2914
2915 ctx.binary_op_splat(op, &mut left, &mut right)?;
2918
2919 match op {
2921 ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => {
2922 right =
2927 ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?;
2928
2929 if !ctx.is_const(right) {
2941 left = ctx.concretize(left)?;
2942 }
2943 }
2944
2945 _ => {
2950 ctx.grow_types(left)?;
2951 ctx.grow_types(right)?;
2952 if let Ok(consensus_scalar) =
2953 ctx.automatic_conversion_consensus(None, [left, right].iter())
2954 {
2955 ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;
2956 ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;
2957 }
2958 }
2959 }
2960
2961 Ok(Typed::Plain(ir::Expression::Binary { op, left, right }))
2962 }
2963 }
2964
2965 #[allow(clippy::too_many_arguments)]
2967 fn call_builtin<'phrase>(
2968 &mut self,
2969 function_name: &'source str,
2970 function_span: Span,
2971 arguments: &[Handle<ast::Expression<'source>>],
2972 template_params: &mut TemplateListIter<'phrase, 'source>,
2973 call_span: Span,
2974 ctx: &mut ExpressionContext<'source, '_, '_>,
2975 is_statement: bool,
2976 ) -> Result<'source, Option<(Handle<ir::Expression>, MustUse)>> {
2977 let (expr, must_use) = if let Some(fun) = conv::map_relational_fun(function_name) {
2978 let mut args = ctx.prepare_args(arguments, 1, function_span);
2979 let argument = self.expression(args.next()?, ctx)?;
2980 args.finish()?;
2981
2982 let argument_unmodified = matches!(
2984 fun,
2985 ir::RelationalFunction::All | ir::RelationalFunction::Any
2986 ) && {
2987 matches!(
2988 resolve_inner!(ctx, argument),
2989 &ir::TypeInner::Scalar(ir::Scalar {
2990 kind: ir::ScalarKind::Bool,
2991 ..
2992 })
2993 )
2994 };
2995
2996 if argument_unmodified {
2997 return Ok(Some((argument, MustUse::Yes)));
2998 } else {
2999 (ir::Expression::Relational { fun, argument }, MustUse::Yes)
3000 }
3001 } else if let Some((axis, ctrl)) = conv::map_derivative(function_name) {
3002 let mut args = ctx.prepare_args(arguments, 1, function_span);
3003 let expr = self.expression(args.next()?, ctx)?;
3004 args.finish()?;
3005
3006 (
3007 ir::Expression::Derivative { axis, ctrl, expr },
3008 MustUse::Yes,
3009 )
3010 } else if let Some(fun) = conv::map_standard_fun(function_name) {
3011 (
3012 self.math_function_helper(function_span, fun, arguments, ctx)?,
3013 MustUse::Yes,
3014 )
3015 } else if let Some(fun) = Texture::map(function_name) {
3016 (
3017 self.texture_sample_helper(fun, arguments, function_span, ctx)?,
3018 MustUse::Yes,
3019 )
3020 } else if let Some((op, cop)) = conv::map_subgroup_operation(function_name) {
3021 return Ok(Some((
3022 self.subgroup_operation_helper(function_span, op, cop, arguments, ctx)?,
3023 MustUse::Yes,
3024 )));
3025 } else if let Some(mode) = SubgroupGather::map(function_name) {
3026 return Ok(Some((
3027 self.subgroup_gather_helper(function_span, mode, arguments, ctx)?,
3028 MustUse::Yes,
3029 )));
3030 } else if let Some(fun) = ir::AtomicFunction::map(function_name) {
3031 return Ok(self
3032 .atomic_helper(function_span, fun, arguments, is_statement, ctx)?
3033 .map(|result| (result, MustUse::No)));
3034 } else {
3035 match function_name {
3036 "bitcast" => {
3037 let ty = template_params.ty(self, ctx)?;
3038
3039 let mut args = ctx.prepare_args(arguments, 1, function_span);
3040 let expr = self.expression(args.next()?, ctx)?;
3041 args.finish()?;
3042
3043 let element_scalar = match ctx.module.types[ty].inner {
3044 ir::TypeInner::Scalar(scalar) => scalar,
3045 ir::TypeInner::Vector { scalar, .. } => scalar,
3046 _ => {
3047 let ty_resolution = resolve!(ctx, expr);
3048 return Err(Box::new(Error::BadTypeCast {
3049 from_type: ctx.type_resolution_to_string(ty_resolution),
3050 span: function_span,
3051 to_type: ctx.type_to_string(ty),
3052 }));
3053 }
3054 };
3055
3056 (
3057 ir::Expression::As {
3058 expr,
3059 kind: element_scalar.kind,
3060 convert: None,
3061 },
3062 MustUse::Yes,
3063 )
3064 }
3065 "coopLoad" | "coopLoadT" => {
3066 let row_major = function_name.ends_with("T");
3067 let (matrix_ty, matrix_span) = template_params.ty_with_span(self, ctx)?;
3068
3069 let mut args = ctx.prepare_args(arguments, 1, call_span);
3070 let pointer = self.expression(args.next()?, ctx)?;
3071 let (columns, rows, role) = match ctx.module.types[matrix_ty].inner {
3072 ir::TypeInner::CooperativeMatrix {
3073 columns,
3074 rows,
3075 role,
3076 ..
3077 } => (columns, rows, role),
3078 _ => return Err(Box::new(Error::InvalidCooperativeLoadType(matrix_span))),
3079 };
3080 let stride = if args.total_args > 1 {
3081 self.expression(args.next()?, ctx)?
3082 } else {
3083 let stride = if row_major {
3085 columns as u32
3086 } else {
3087 rows as u32
3088 };
3089 ctx.append_expression(
3090 ir::Expression::Literal(ir::Literal::U32(stride)),
3091 Span::UNDEFINED,
3092 )?
3093 };
3094 args.finish()?;
3095
3096 (
3097 crate::Expression::CooperativeLoad {
3098 columns,
3099 rows,
3100 role,
3101 data: crate::CooperativeData {
3102 pointer,
3103 stride,
3104 row_major,
3105 },
3106 },
3107 MustUse::Yes,
3108 )
3109 }
3110 "select" => {
3111 let mut args = ctx.prepare_args(arguments, 3, function_span);
3112
3113 let reject_orig = args.next()?;
3114 let accept_orig = args.next()?;
3115 let mut values = [
3116 self.expression_for_abstract(reject_orig, ctx)?,
3117 self.expression_for_abstract(accept_orig, ctx)?,
3118 ];
3119 let condition = self.expression(args.next()?, ctx)?;
3120
3121 args.finish()?;
3122
3123 let diagnostic_details =
3124 |ctx: &ExpressionContext<'_, '_, '_>,
3125 ty_res: &proc::TypeResolution,
3126 orig_expr| {
3127 (
3128 ctx.ast_expressions.get_span(orig_expr),
3129 format!("`{}`", ctx.as_diagnostic_display(ty_res)),
3130 )
3131 };
3132 for (&value, orig_value) in values.iter().zip([reject_orig, accept_orig]) {
3133 let value_ty_res = resolve!(ctx, value);
3134 if value_ty_res
3135 .inner_with(&ctx.module.types)
3136 .vector_size_and_scalar()
3137 .is_none()
3138 {
3139 let (arg_span, arg_type) =
3140 diagnostic_details(ctx, value_ty_res, orig_value);
3141 return Err(Box::new(Error::SelectUnexpectedArgumentType {
3142 arg_span,
3143 arg_type,
3144 }));
3145 }
3146 }
3147 let mut consensus_scalar = ctx
3148 .automatic_conversion_consensus(None, &values)
3149 .map_err(|_idx| {
3150 let [reject, accept] = values;
3151 let [(reject_span, reject_type), (accept_span, accept_type)] =
3152 [(reject_orig, reject), (accept_orig, accept)].map(
3153 |(orig_expr, expr)| {
3154 let ty_res = &ctx.typifier()[expr];
3155 diagnostic_details(ctx, ty_res, orig_expr)
3156 },
3157 );
3158 Error::SelectRejectAndAcceptHaveNoCommonType {
3159 reject_span,
3160 reject_type,
3161 accept_span,
3162 accept_type,
3163 }
3164 })?;
3165 if !ctx.is_const(condition) {
3166 consensus_scalar = consensus_scalar.concretize();
3167 }
3168
3169 ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;
3170
3171 let [reject, accept] = values;
3172
3173 (
3174 ir::Expression::Select {
3175 reject,
3176 accept,
3177 condition,
3178 },
3179 MustUse::Yes,
3180 )
3181 }
3182 "arrayLength" => {
3183 let mut args = ctx.prepare_args(arguments, 1, function_span);
3184 let expr = self.expression(args.next()?, ctx)?;
3185 args.finish()?;
3186
3187 (ir::Expression::ArrayLength(expr), MustUse::Yes)
3188 }
3189 "atomicLoad" => {
3190 let mut args = ctx.prepare_args(arguments, 1, function_span);
3191 let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?;
3192 args.finish()?;
3193
3194 (ir::Expression::Load { pointer }, MustUse::No)
3195 }
3196 "atomicStore" => {
3197 let mut args = ctx.prepare_args(arguments, 2, function_span);
3198 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3199 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3200 args.finish()?;
3201
3202 let rctx = ctx.runtime_expression_ctx(function_span)?;
3203 rctx.block
3204 .extend(rctx.emitter.finish(&rctx.function.expressions));
3205 rctx.emitter.start(&rctx.function.expressions);
3206 rctx.block
3207 .push(ir::Statement::Store { pointer, value }, function_span);
3208 return Ok(None);
3209 }
3210 "atomicCompareExchangeWeak" => {
3211 let mut args = ctx.prepare_args(arguments, 3, function_span);
3212
3213 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3214
3215 let compare = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3216
3217 let value = args.next()?;
3218 let value_span = ctx.ast_expressions.get_span(value);
3219 let value = self.expression_with_leaf_scalar(value, scalar, ctx)?;
3220
3221 args.finish()?;
3222
3223 let expression = match *resolve_inner!(ctx, value) {
3224 ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult {
3225 ty: ctx.module.generate_predeclared_type(
3226 ir::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
3227 ),
3228 comparison: true,
3229 },
3230 _ => return Err(Box::new(Error::InvalidAtomicOperandType(value_span))),
3231 };
3232
3233 let result = ctx.interrupt_emitter(expression, function_span)?;
3234 let rctx = ctx.runtime_expression_ctx(function_span)?;
3235 rctx.block.push(
3236 ir::Statement::Atomic {
3237 pointer,
3238 fun: ir::AtomicFunction::Exchange {
3239 compare: Some(compare),
3240 },
3241 value,
3242 result: Some(result),
3243 },
3244 function_span,
3245 );
3246 return Ok(Some((result, MustUse::No)));
3247 }
3248 "textureAtomicMin" | "textureAtomicMax" | "textureAtomicAdd"
3249 | "textureAtomicAnd" | "textureAtomicOr" | "textureAtomicXor" => {
3250 let mut args = ctx.prepare_args(arguments, 3, function_span);
3251
3252 let image = args.next()?;
3253 let image_span = ctx.ast_expressions.get_span(image);
3254 let image = self.expression(image, ctx)?;
3255
3256 let coordinate = self.expression(args.next()?, ctx)?;
3257
3258 let (_, arrayed) = ctx.image_data(image, image_span)?;
3259 let array_index = arrayed
3260 .then(|| {
3261 args.min_args += 1;
3262 self.expression(args.next()?, ctx)
3263 })
3264 .transpose()?;
3265
3266 let value = self.expression(args.next()?, ctx)?;
3267
3268 args.finish()?;
3269
3270 let rctx = ctx.runtime_expression_ctx(function_span)?;
3271 rctx.block
3272 .extend(rctx.emitter.finish(&rctx.function.expressions));
3273 rctx.emitter.start(&rctx.function.expressions);
3274 let stmt = ir::Statement::ImageAtomic {
3275 image,
3276 coordinate,
3277 array_index,
3278 fun: match function_name {
3279 "textureAtomicMin" => ir::AtomicFunction::Min,
3280 "textureAtomicMax" => ir::AtomicFunction::Max,
3281 "textureAtomicAdd" => ir::AtomicFunction::Add,
3282 "textureAtomicAnd" => ir::AtomicFunction::And,
3283 "textureAtomicOr" => ir::AtomicFunction::InclusiveOr,
3284 "textureAtomicXor" => ir::AtomicFunction::ExclusiveOr,
3285 _ => unreachable!(),
3286 },
3287 value,
3288 };
3289 rctx.block.push(stmt, function_span);
3290 return Ok(None);
3291 }
3292 "storageBarrier" => {
3293 ctx.prepare_args(arguments, 0, function_span).finish()?;
3294
3295 let rctx = ctx.runtime_expression_ctx(function_span)?;
3296 rctx.block.push(
3297 ir::Statement::ControlBarrier(ir::Barrier::STORAGE),
3298 function_span,
3299 );
3300 return Ok(None);
3301 }
3302 "workgroupBarrier" => {
3303 ctx.prepare_args(arguments, 0, function_span).finish()?;
3304
3305 let rctx = ctx.runtime_expression_ctx(function_span)?;
3306 rctx.block.push(
3307 ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP),
3308 function_span,
3309 );
3310 return Ok(None);
3311 }
3312 "subgroupBarrier" => {
3313 ctx.prepare_args(arguments, 0, function_span).finish()?;
3314
3315 let rctx = ctx.runtime_expression_ctx(function_span)?;
3316 rctx.block.push(
3317 ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP),
3318 function_span,
3319 );
3320 return Ok(None);
3321 }
3322 "textureBarrier" => {
3323 ctx.prepare_args(arguments, 0, function_span).finish()?;
3324
3325 let rctx = ctx.runtime_expression_ctx(function_span)?;
3326 rctx.block.push(
3327 ir::Statement::ControlBarrier(ir::Barrier::TEXTURE),
3328 function_span,
3329 );
3330 return Ok(None);
3331 }
3332 "workgroupUniformLoad" => {
3333 let mut args = ctx.prepare_args(arguments, 1, function_span);
3334 let expr = args.next()?;
3335 args.finish()?;
3336
3337 let pointer = self.expression(expr, ctx)?;
3338 let result_ty = match *resolve_inner!(ctx, pointer) {
3339 ir::TypeInner::Pointer {
3340 base,
3341 space: ir::AddressSpace::WorkGroup,
3342 } => match ctx.module.types[base].inner {
3343 ir::TypeInner::Atomic(scalar) => ctx.module.types.insert(
3346 ir::Type {
3347 name: None,
3348 inner: ir::TypeInner::Scalar(scalar),
3349 },
3350 function_span,
3351 ),
3352 _ => base,
3353 },
3354 ir::TypeInner::ValuePointer {
3355 size,
3356 scalar,
3357 space: ir::AddressSpace::WorkGroup,
3358 } => ctx.module.types.insert(
3359 ir::Type {
3360 name: None,
3361 inner: match size {
3362 Some(size) => ir::TypeInner::Vector { size, scalar },
3363 None => ir::TypeInner::Scalar(scalar),
3364 },
3365 },
3366 function_span,
3367 ),
3368 _ => {
3369 let span = ctx.ast_expressions.get_span(expr);
3370 return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span)));
3371 }
3372 };
3373 let result = ctx.interrupt_emitter(
3374 ir::Expression::WorkGroupUniformLoadResult { ty: result_ty },
3375 function_span,
3376 )?;
3377 let rctx = ctx.runtime_expression_ctx(function_span)?;
3378 rctx.block.push(
3379 ir::Statement::WorkGroupUniformLoad { pointer, result },
3380 function_span,
3381 );
3382
3383 return Ok(Some((result, MustUse::Yes)));
3384 }
3385 "textureStore" => {
3386 let mut args = ctx.prepare_args(arguments, 3, function_span);
3387
3388 let image = args.next()?;
3389 let image_span = ctx.ast_expressions.get_span(image);
3390 let image = self.expression(image, ctx)?;
3391
3392 let coordinate = self.expression(args.next()?, ctx)?;
3393
3394 let (class, arrayed) = ctx.image_data(image, image_span)?;
3395 let array_index = arrayed
3396 .then(|| {
3397 args.min_args += 1;
3398 self.expression(args.next()?, ctx)
3399 })
3400 .transpose()?;
3401 let scalar = if let ir::ImageClass::Storage { format, .. } = class {
3402 format.into()
3403 } else {
3404 return Err(Box::new(Error::NotStorageTexture(image_span)));
3405 };
3406
3407 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3408
3409 args.finish()?;
3410
3411 let rctx = ctx.runtime_expression_ctx(function_span)?;
3412 rctx.block
3413 .extend(rctx.emitter.finish(&rctx.function.expressions));
3414 rctx.emitter.start(&rctx.function.expressions);
3415 let stmt = ir::Statement::ImageStore {
3416 image,
3417 coordinate,
3418 array_index,
3419 value,
3420 };
3421 rctx.block.push(stmt, function_span);
3422 return Ok(None);
3423 }
3424 "textureLoad" => {
3425 let mut args = ctx.prepare_args(arguments, 2, function_span);
3426
3427 let image = args.next()?;
3428 let image_span = ctx.ast_expressions.get_span(image);
3429 let image = self.expression(image, ctx)?;
3430
3431 let coordinate = self.expression(args.next()?, ctx)?;
3432
3433 let (class, arrayed) = ctx.image_data(image, image_span)?;
3434 let array_index = arrayed
3435 .then(|| {
3436 args.min_args += 1;
3437 self.expression(args.next()?, ctx)
3438 })
3439 .transpose()?;
3440
3441 let level = class
3442 .is_mipmapped()
3443 .then(|| {
3444 args.min_args += 1;
3445 self.expression(args.next()?, ctx)
3446 })
3447 .transpose()?;
3448
3449 let sample = class
3450 .is_multisampled()
3451 .then(|| self.expression(args.next()?, ctx))
3452 .transpose()?;
3453
3454 args.finish()?;
3455
3456 (
3457 ir::Expression::ImageLoad {
3458 image,
3459 coordinate,
3460 array_index,
3461 level,
3462 sample,
3463 },
3464 MustUse::Yes,
3465 )
3466 }
3467 "textureDimensions" => {
3468 let mut args = ctx.prepare_args(arguments, 1, function_span);
3469 let image = self.expression(args.next()?, ctx)?;
3470 let level = args
3471 .next()
3472 .map(|arg| self.expression(arg, ctx))
3473 .ok()
3474 .transpose()?;
3475 args.finish()?;
3476
3477 (
3478 ir::Expression::ImageQuery {
3479 image,
3480 query: ir::ImageQuery::Size { level },
3481 },
3482 MustUse::Yes,
3483 )
3484 }
3485 "textureNumLevels" => {
3486 let mut args = ctx.prepare_args(arguments, 1, function_span);
3487 let image = self.expression(args.next()?, ctx)?;
3488 args.finish()?;
3489
3490 (
3491 ir::Expression::ImageQuery {
3492 image,
3493 query: ir::ImageQuery::NumLevels,
3494 },
3495 MustUse::Yes,
3496 )
3497 }
3498 "textureNumLayers" => {
3499 let mut args = ctx.prepare_args(arguments, 1, function_span);
3500 let image = self.expression(args.next()?, ctx)?;
3501 args.finish()?;
3502
3503 (
3504 ir::Expression::ImageQuery {
3505 image,
3506 query: ir::ImageQuery::NumLayers,
3507 },
3508 MustUse::Yes,
3509 )
3510 }
3511 "textureNumSamples" => {
3512 let mut args = ctx.prepare_args(arguments, 1, function_span);
3513 let image = self.expression(args.next()?, ctx)?;
3514 args.finish()?;
3515
3516 (
3517 ir::Expression::ImageQuery {
3518 image,
3519 query: ir::ImageQuery::NumSamples,
3520 },
3521 MustUse::Yes,
3522 )
3523 }
3524 "rayQueryInitialize" => {
3525 let mut args = ctx.prepare_args(arguments, 3, function_span);
3526 let query = self.ray_query_pointer(args.next()?, ctx)?;
3527 let acceleration_structure = self.expression(args.next()?, ctx)?;
3528 let descriptor = self.expression(args.next()?, ctx)?;
3529 args.finish()?;
3530
3531 let _ = ctx.module.generate_ray_desc_type();
3532 let fun = ir::RayQueryFunction::Initialize {
3533 acceleration_structure,
3534 descriptor,
3535 };
3536
3537 let rctx = ctx.runtime_expression_ctx(function_span)?;
3538 rctx.block
3539 .extend(rctx.emitter.finish(&rctx.function.expressions));
3540 rctx.emitter.start(&rctx.function.expressions);
3541 rctx.block
3542 .push(ir::Statement::RayQuery { query, fun }, function_span);
3543 return Ok(None);
3544 }
3545 "getCommittedHitVertexPositions" => {
3546 let mut args = ctx.prepare_args(arguments, 1, function_span);
3547 let query = self.ray_query_pointer(args.next()?, ctx)?;
3548 args.finish()?;
3549
3550 let _ = ctx.module.generate_vertex_return_type();
3551
3552 (
3553 ir::Expression::RayQueryVertexPositions {
3554 query,
3555 committed: true,
3556 },
3557 MustUse::No,
3558 )
3559 }
3560 "getCandidateHitVertexPositions" => {
3561 let mut args = ctx.prepare_args(arguments, 1, function_span);
3562 let query = self.ray_query_pointer(args.next()?, ctx)?;
3563 args.finish()?;
3564
3565 let _ = ctx.module.generate_vertex_return_type();
3566
3567 (
3568 ir::Expression::RayQueryVertexPositions {
3569 query,
3570 committed: false,
3571 },
3572 MustUse::No,
3573 )
3574 }
3575 "rayQueryProceed" => {
3576 let mut args = ctx.prepare_args(arguments, 1, function_span);
3577 let query = self.ray_query_pointer(args.next()?, ctx)?;
3578 args.finish()?;
3579
3580 let result = ctx
3581 .interrupt_emitter(ir::Expression::RayQueryProceedResult, function_span)?;
3582 let fun = ir::RayQueryFunction::Proceed { result };
3583 let rctx = ctx.runtime_expression_ctx(function_span)?;
3584 rctx.block
3585 .push(ir::Statement::RayQuery { query, fun }, function_span);
3586 return Ok(Some((result, MustUse::No)));
3587 }
3588 "rayQueryGenerateIntersection" => {
3589 let mut args = ctx.prepare_args(arguments, 2, function_span);
3590 let query = self.ray_query_pointer(args.next()?, ctx)?;
3591 let hit_t = self.expression(args.next()?, ctx)?;
3592 args.finish()?;
3593
3594 let fun = ir::RayQueryFunction::GenerateIntersection { hit_t };
3595 let rctx = ctx.runtime_expression_ctx(function_span)?;
3596 rctx.block
3597 .push(ir::Statement::RayQuery { query, fun }, function_span);
3598 return Ok(None);
3599 }
3600 "rayQueryConfirmIntersection" => {
3601 let mut args = ctx.prepare_args(arguments, 1, function_span);
3602 let query = self.ray_query_pointer(args.next()?, ctx)?;
3603 args.finish()?;
3604
3605 let fun = ir::RayQueryFunction::ConfirmIntersection;
3606 let rctx = ctx.runtime_expression_ctx(function_span)?;
3607 rctx.block
3608 .push(ir::Statement::RayQuery { query, fun }, function_span);
3609 return Ok(None);
3610 }
3611 "rayQueryTerminate" => {
3612 let mut args = ctx.prepare_args(arguments, 1, function_span);
3613 let query = self.ray_query_pointer(args.next()?, ctx)?;
3614 args.finish()?;
3615
3616 let fun = ir::RayQueryFunction::Terminate;
3617 let rctx = ctx.runtime_expression_ctx(function_span)?;
3618 rctx.block
3619 .push(ir::Statement::RayQuery { query, fun }, function_span);
3620 return Ok(None);
3621 }
3622 "rayQueryGetCommittedIntersection" => {
3623 let mut args = ctx.prepare_args(arguments, 1, function_span);
3624 let query = self.ray_query_pointer(args.next()?, ctx)?;
3625 args.finish()?;
3626
3627 let _ = ctx.module.generate_ray_intersection_type();
3628 (
3629 ir::Expression::RayQueryGetIntersection {
3630 query,
3631 committed: true,
3632 },
3633 MustUse::No,
3634 )
3635 }
3636 "rayQueryGetCandidateIntersection" => {
3637 let mut args = ctx.prepare_args(arguments, 1, function_span);
3638 let query = self.ray_query_pointer(args.next()?, ctx)?;
3639 args.finish()?;
3640
3641 let _ = ctx.module.generate_ray_intersection_type();
3642 (
3643 ir::Expression::RayQueryGetIntersection {
3644 query,
3645 committed: false,
3646 },
3647 MustUse::No,
3648 )
3649 }
3650 "subgroupBallot" => {
3651 let mut args = ctx.prepare_args(arguments, 0, function_span);
3652 let predicate = if arguments.len() == 1 {
3653 Some(self.expression(args.next()?, ctx)?)
3654 } else {
3655 None
3656 };
3657 args.finish()?;
3658
3659 let result =
3660 ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, function_span)?;
3661 let rctx = ctx.runtime_expression_ctx(function_span)?;
3662 rctx.block.push(
3663 ir::Statement::SubgroupBallot { result, predicate },
3664 function_span,
3665 );
3666 return Ok(Some((result, MustUse::Yes)));
3667 }
3668 "quadSwapX" => {
3669 let mut args = ctx.prepare_args(arguments, 1, function_span);
3670
3671 let argument = self.expression(args.next()?, ctx)?;
3672 args.finish()?;
3673
3674 let ty = ctx.register_type(argument)?;
3675
3676 let result = ctx.interrupt_emitter(
3677 crate::Expression::SubgroupOperationResult { ty },
3678 function_span,
3679 )?;
3680 let rctx = ctx.runtime_expression_ctx(function_span)?;
3681 rctx.block.push(
3682 crate::Statement::SubgroupGather {
3683 mode: crate::GatherMode::QuadSwap(crate::Direction::X),
3684 argument,
3685 result,
3686 },
3687 function_span,
3688 );
3689 return Ok(Some((result, MustUse::Yes)));
3690 }
3691 "quadSwapY" => {
3692 let mut args = ctx.prepare_args(arguments, 1, function_span);
3693
3694 let argument = self.expression(args.next()?, ctx)?;
3695 args.finish()?;
3696
3697 let ty = ctx.register_type(argument)?;
3698
3699 let result = ctx.interrupt_emitter(
3700 crate::Expression::SubgroupOperationResult { ty },
3701 function_span,
3702 )?;
3703 let rctx = ctx.runtime_expression_ctx(function_span)?;
3704 rctx.block.push(
3705 crate::Statement::SubgroupGather {
3706 mode: crate::GatherMode::QuadSwap(crate::Direction::Y),
3707 argument,
3708 result,
3709 },
3710 function_span,
3711 );
3712 return Ok(Some((result, MustUse::Yes)));
3713 }
3714 "quadSwapDiagonal" => {
3715 let mut args = ctx.prepare_args(arguments, 1, function_span);
3716
3717 let argument = self.expression(args.next()?, ctx)?;
3718 args.finish()?;
3719
3720 let ty = ctx.register_type(argument)?;
3721
3722 let result = ctx.interrupt_emitter(
3723 crate::Expression::SubgroupOperationResult { ty },
3724 function_span,
3725 )?;
3726 let rctx = ctx.runtime_expression_ctx(function_span)?;
3727 rctx.block.push(
3728 crate::Statement::SubgroupGather {
3729 mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal),
3730 argument,
3731 result,
3732 },
3733 function_span,
3734 );
3735 return Ok(Some((result, MustUse::Yes)));
3736 }
3737 "coopStore" | "coopStoreT" => {
3738 let row_major = function_name.ends_with("T");
3739
3740 let mut args = ctx.prepare_args(arguments, 2, function_span);
3741 let target = self.expression(args.next()?, ctx)?;
3742 let pointer = self.expression(args.next()?, ctx)?;
3743 let stride = if args.total_args > 2 {
3744 self.expression(args.next()?, ctx)?
3745 } else {
3746 let stride = match *resolve_inner!(ctx, target) {
3748 ir::TypeInner::CooperativeMatrix { columns, rows, .. } => {
3749 if row_major {
3750 columns as u32
3751 } else {
3752 rows as u32
3753 }
3754 }
3755 _ => 0,
3756 };
3757 ctx.append_expression(
3758 ir::Expression::Literal(ir::Literal::U32(stride)),
3759 Span::UNDEFINED,
3760 )?
3761 };
3762 args.finish()?;
3763
3764 let rctx = ctx.runtime_expression_ctx(function_span)?;
3765 rctx.block.push(
3766 crate::Statement::CooperativeStore {
3767 target,
3768 data: crate::CooperativeData {
3769 pointer,
3770 stride,
3771 row_major,
3772 },
3773 },
3774 function_span,
3775 );
3776 return Ok(None);
3777 }
3778 "coopMultiplyAdd" => {
3779 let mut args = ctx.prepare_args(arguments, 3, function_span);
3780 let a = self.expression(args.next()?, ctx)?;
3781 let b = self.expression(args.next()?, ctx)?;
3782 let c = self.expression(args.next()?, ctx)?;
3783 args.finish()?;
3784
3785 (
3786 ir::Expression::CooperativeMultiplyAdd { a, b, c },
3787 MustUse::Yes,
3788 )
3789 }
3790 "traceRay" => {
3791 let mut args = ctx.prepare_args(arguments, 3, function_span);
3792 let acceleration_structure = self.expression(args.next()?, ctx)?;
3793 let descriptor = self.expression(args.next()?, ctx)?;
3794 let payload = self.expression(args.next()?, ctx)?;
3795 args.finish()?;
3796
3797 let _ = ctx.module.generate_ray_desc_type();
3798 let fun = ir::RayPipelineFunction::TraceRay {
3799 acceleration_structure,
3800 descriptor,
3801 payload,
3802 };
3803
3804 let rctx = ctx.runtime_expression_ctx(function_span)?;
3805 rctx.block
3806 .extend(rctx.emitter.finish(&rctx.function.expressions));
3807 rctx.emitter.start(&rctx.function.expressions);
3808 rctx.block
3809 .push(ir::Statement::RayPipelineFunction(fun), function_span);
3810 return Ok(None);
3811 }
3812 _ => return Err(Box::new(Error::UnknownIdent(function_span, function_name))),
3813 }
3814 };
3815
3816 let expr = ctx.append_expression(expr, function_span)?;
3817 Ok(Some((expr, must_use)))
3818 }
3819
3820 fn call(
3839 &mut self,
3840 call_phrase: &ast::CallPhrase<'source>,
3841 span: Span,
3842 ctx: &mut ExpressionContext<'source, '_, '_>,
3843 is_statement: bool,
3844 ) -> Result<'source, Option<Handle<ir::Expression>>> {
3845 let function_name = match call_phrase.function.ident {
3846 ast::IdentExpr::Unresolved(name) => name,
3847 ast::IdentExpr::Local(_) => {
3848 return Err(Box::new(Error::CalledLocalDecl(
3849 call_phrase.function.ident_span,
3850 )))
3851 }
3852 };
3853 let mut function_span = call_phrase.function.ident_span;
3854 function_span.subsume(call_phrase.function.template_list_span);
3855 let arguments = call_phrase.arguments.as_slice();
3856
3857 let mut tl = TemplateListIter::new(function_span, &call_phrase.function.template_list);
3858
3859 let result = match ctx.globals.get(function_name) {
3860 Some(&LoweredGlobalDecl::Type(ty)) => {
3861 tl.finish(ctx)?;
3863
3864 let handle =
3865 self.construct(span, Constructor::Type(ty), function_span, arguments, ctx)?;
3866 Some((handle, MustUse::Yes))
3867 }
3868 Some(
3869 &LoweredGlobalDecl::Const(_)
3870 | &LoweredGlobalDecl::Override(_)
3871 | &LoweredGlobalDecl::Var(_),
3872 ) => {
3873 return Err(Box::new(Error::Unexpected(
3874 function_span,
3875 ExpectedToken::Function,
3876 )))
3877 }
3878 Some(&LoweredGlobalDecl::EntryPoint(_)) => {
3879 return Err(Box::new(Error::CalledEntryPoint(function_span)));
3880 }
3881 Some(&LoweredGlobalDecl::Function {
3882 handle: function,
3883 must_use,
3884 }) => {
3885 tl.finish(ctx)?;
3887
3888 let arguments = arguments
3889 .iter()
3890 .enumerate()
3891 .map(|(i, &arg)| {
3892 let Some(&ir::FunctionArgument {
3894 ty: parameter_ty, ..
3895 }) = ctx.module.functions[function].arguments.get(i)
3896 else {
3897 return self.expression(arg, ctx);
3900 };
3901
3902 let expr = self.expression_for_abstract(arg, ctx)?;
3903 ctx.try_automatic_conversions(
3904 expr,
3905 &proc::TypeResolution::Handle(parameter_ty),
3906 ctx.ast_expressions.get_span(arg),
3907 )
3908 })
3909 .collect::<Result<Vec<_>>>()?;
3910
3911 let has_result = ctx.module.functions[function].result.is_some();
3912
3913 let rctx = ctx.runtime_expression_ctx(span)?;
3914 rctx.block
3916 .extend(rctx.emitter.finish(&rctx.function.expressions));
3917 let result = has_result.then(|| {
3918 let result = rctx
3919 .function
3920 .expressions
3921 .append(ir::Expression::CallResult(function), span);
3922 rctx.local_expression_kind_tracker
3923 .insert(result, proc::ExpressionKind::Runtime);
3924 (result, must_use.into())
3925 });
3926 rctx.emitter.start(&rctx.function.expressions);
3927 rctx.block.push(
3928 ir::Statement::Call {
3929 function,
3930 arguments,
3931 result: result.map(|(expr, _)| expr),
3932 },
3933 span,
3934 );
3935
3936 result
3937 }
3938 None => {
3939 let ty = conv::map_predeclared_type(
3941 &ctx.enable_extensions,
3942 function_span,
3943 function_name,
3944 )?;
3945 if let Some(ty) = ty {
3946 let empty_template_list = call_phrase.function.template_list.is_empty();
3947 let constructor_ty = match ty {
3948 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Vector {
3949 size,
3950 }) if empty_template_list => Constructor::PartialVector { size },
3951 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Matrix {
3952 columns,
3953 rows,
3954 }) if empty_template_list => Constructor::PartialMatrix { columns, rows },
3955 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Array)
3956 if empty_template_list =>
3957 {
3958 Constructor::PartialArray
3959 }
3960 conv::PredeclaredType::TypeGenerator(
3961 conv::TypeGenerator::CooperativeMatrix { .. },
3962 ) if empty_template_list => {
3963 return Err(Box::new(Error::UnderspecifiedCooperativeMatrix));
3964 }
3965 _ => Constructor::Type(self.finalize_type(ctx, ty, &mut tl, None)?),
3966 };
3967 tl.finish(ctx)?;
3968 let handle =
3969 self.construct(span, constructor_ty, function_span, arguments, ctx)?;
3970 Some((handle, MustUse::Yes))
3971 } else {
3972 let result = self.call_builtin(
3974 function_name,
3975 function_span,
3976 arguments,
3977 &mut tl,
3978 span,
3979 ctx,
3980 is_statement,
3981 )?;
3982 tl.finish(ctx)?;
3983 result
3984 }
3985 }
3986 };
3987
3988 let result_used = !is_statement;
3989 if matches!(result, Some((_, MustUse::Yes))) && !result_used {
3990 return Err(Box::new(Error::FunctionMustUseUnused(function_span)));
3991 }
3992 Ok(result.map(|(expr, _)| expr))
3993 }
3994
3995 fn math_function_helper(
4006 &mut self,
4007 span: Span,
4008 fun: ir::MathFunction,
4009 ast_arguments: &[Handle<ast::Expression<'source>>],
4010 ctx: &mut ExpressionContext<'source, '_, '_>,
4011 ) -> Result<'source, ir::Expression> {
4012 let mut lowered_arguments = Vec::with_capacity(ast_arguments.len());
4013 for &arg in ast_arguments {
4014 let lowered = self.expression_for_abstract(arg, ctx)?;
4015 ctx.grow_types(lowered)?;
4016 lowered_arguments.push(lowered);
4017 }
4018
4019 let fun_overloads = fun.overloads();
4020 let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?;
4021 self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?;
4022
4023 if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion {
4027 ctx.module.generate_predeclared_type(predeclared);
4028 }
4029
4030 Ok(ir::Expression::Math {
4031 fun,
4032 arg: lowered_arguments[0],
4033 arg1: lowered_arguments.get(1).cloned(),
4034 arg2: lowered_arguments.get(2).cloned(),
4035 arg3: lowered_arguments.get(3).cloned(),
4036 })
4037 }
4038
4039 fn resolve_overloads<O, F>(
4050 &self,
4051 span: Span,
4052 fun: F,
4053 overloads: O,
4054 arguments: &[Handle<ir::Expression>],
4055 ctx: &ExpressionContext<'source, '_, '_>,
4056 ) -> Result<'source, proc::Rule>
4057 where
4058 O: proc::OverloadSet,
4059 F: TryToWgsl + core::fmt::Debug + Copy,
4060 {
4061 let mut remaining_overloads = overloads.clone();
4062 let min_arguments = remaining_overloads.min_arguments();
4063 let max_arguments = remaining_overloads.max_arguments();
4064 if arguments.len() < min_arguments {
4065 return Err(Box::new(Error::WrongArgumentCount {
4066 span,
4067 expected: min_arguments as u32..max_arguments as u32,
4068 found: arguments.len() as u32,
4069 }));
4070 }
4071 if arguments.len() > max_arguments {
4072 return Err(Box::new(Error::TooManyArguments {
4073 function: fun.to_wgsl_for_diagnostics(),
4074 call_span: span,
4075 arg_span: ctx.get_expression_span(arguments[max_arguments]),
4076 max_arguments: max_arguments as _,
4077 }));
4078 }
4079
4080 log::debug!(
4081 "Initial overloads: {:#?}",
4082 remaining_overloads.for_debug(&ctx.module.types)
4083 );
4084
4085 for (arg_index, &arg) in arguments.iter().enumerate() {
4086 let arg_type_resolution = &ctx.typifier()[arg];
4087 let arg_inner = arg_type_resolution.inner_with(&ctx.module.types);
4088 log::debug!(
4089 "Supplying argument {arg_index} of type {:?}",
4090 arg_type_resolution.for_debug(&ctx.module.types)
4091 );
4092 let next_remaining_overloads =
4093 remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types);
4094
4095 log::debug!(
4103 "Remaining overloads: {:#?}",
4104 next_remaining_overloads.for_debug(&ctx.module.types)
4105 );
4106
4107 if next_remaining_overloads.is_empty() {
4110 let function = fun.to_wgsl_for_diagnostics();
4111 let call_span = span;
4112 let arg_span = ctx.get_expression_span(arg);
4113 let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string();
4114
4115 let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types);
4118 if only_this_argument.is_empty() {
4119 let allowed: Vec<String> = overloads
4123 .allowed_args(arg_index, &ctx.module.to_ctx())
4124 .iter()
4125 .map(|ty| ctx.type_resolution_to_string(ty))
4126 .collect();
4127
4128 if allowed.is_empty() {
4129 unreachable!("expected all overloads to have the same arity");
4135 }
4136
4137 return Err(Box::new(Error::WrongArgumentType {
4140 function,
4141 call_span,
4142 arg_span,
4143 arg_index: arg_index as u32,
4144 arg_ty,
4145 allowed,
4146 }));
4147 }
4148
4149 let allowed: Vec<String> = remaining_overloads
4157 .allowed_args(arg_index, &ctx.module.to_ctx())
4158 .iter()
4159 .map(|ty| ctx.type_resolution_to_string(ty))
4160 .collect();
4161
4162 let mut remaining_overloads = overloads;
4165 for (prior_index, &prior_expr) in arguments.iter().enumerate() {
4166 let prior_type_resolution = &ctx.typifier()[prior_expr];
4167 let prior_ty = prior_type_resolution.inner_with(&ctx.module.types);
4168 remaining_overloads =
4169 remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types);
4170 if remaining_overloads
4171 .arg(arg_index, arg_inner, &ctx.module.types)
4172 .is_empty()
4173 {
4174 let inconsistent_span = ctx.get_expression_span(arguments[prior_index]);
4176 let inconsistent_ty =
4177 ctx.as_diagnostic_display(prior_type_resolution).to_string();
4178
4179 if allowed.is_empty() {
4180 unreachable!("expected all overloads to have the same arity");
4187 }
4188
4189 return Err(Box::new(Error::InconsistentArgumentType {
4191 function,
4192 call_span,
4193 arg_span,
4194 arg_index: arg_index as u32,
4195 arg_ty,
4196 inconsistent_span,
4197 inconsistent_index: prior_index as u32,
4198 inconsistent_ty,
4199 allowed,
4200 }));
4201 }
4202 }
4203 unreachable!("Failed to eliminate argument type when re-tried");
4204 }
4205 remaining_overloads = next_remaining_overloads;
4206 }
4207
4208 Ok(remaining_overloads.most_preferred())
4211 }
4212
4213 fn apply_automatic_conversions_for_call(
4219 &self,
4220 rule: &proc::Rule,
4221 arguments: &mut [Handle<ir::Expression>],
4222 ctx: &mut ExpressionContext<'source, '_, '_>,
4223 ) -> Result<'source, ()> {
4224 for (i, argument) in arguments.iter_mut().enumerate() {
4225 let goal_inner = rule.arguments[i].inner_with(&ctx.module.types);
4226 let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) {
4227 Some(goal_scalar) => {
4228 let arg_span = ctx.get_expression_span(*argument);
4229 ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)?
4230 }
4231 None => *argument,
4233 };
4234
4235 *argument = converted;
4236 }
4237
4238 Ok(())
4239 }
4240
4241 fn atomic_pointer(
4242 &mut self,
4243 expr: Handle<ast::Expression<'source>>,
4244 ctx: &mut ExpressionContext<'source, '_, '_>,
4245 ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> {
4246 let span = ctx.ast_expressions.get_span(expr);
4247 let pointer = self.expression(expr, ctx)?;
4248
4249 match *resolve_inner!(ctx, pointer) {
4250 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4251 ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)),
4252 ref other => {
4253 log::error!("Pointer type to {other:?} passed to atomic op");
4254 Err(Box::new(Error::InvalidAtomicPointer(span)))
4255 }
4256 },
4257 ref other => {
4258 log::error!("Type {other:?} passed to atomic op");
4259 Err(Box::new(Error::InvalidAtomicPointer(span)))
4260 }
4261 }
4262 }
4263
4264 fn atomic_helper(
4265 &mut self,
4266 span: Span,
4267 fun: ir::AtomicFunction,
4268 args: &[Handle<ast::Expression<'source>>],
4269 is_statement: bool,
4270 ctx: &mut ExpressionContext<'source, '_, '_>,
4271 ) -> Result<'source, Option<Handle<ir::Expression>>> {
4272 let mut args = ctx.prepare_args(args, 2, span);
4273
4274 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
4275 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
4276 let value_inner = resolve_inner!(ctx, value);
4277 args.finish()?;
4278
4279 let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max)
4284 && matches!(
4285 *value_inner,
4286 ir::TypeInner::Scalar(ir::Scalar { width: 8, .. })
4287 );
4288 let result = if is_64_bit_min_max && is_statement {
4289 let rctx = ctx.runtime_expression_ctx(span)?;
4290 rctx.block
4291 .extend(rctx.emitter.finish(&rctx.function.expressions));
4292 rctx.emitter.start(&rctx.function.expressions);
4293 None
4294 } else {
4295 let ty = ctx.register_type(value)?;
4296 Some(ctx.interrupt_emitter(
4297 ir::Expression::AtomicResult {
4298 ty,
4299 comparison: false,
4300 },
4301 span,
4302 )?)
4303 };
4304 let rctx = ctx.runtime_expression_ctx(span)?;
4305 rctx.block.push(
4306 ir::Statement::Atomic {
4307 pointer,
4308 fun,
4309 value,
4310 result,
4311 },
4312 span,
4313 );
4314 Ok(result)
4315 }
4316
4317 fn texture_sample_helper(
4318 &mut self,
4319 fun: Texture,
4320 args: &[Handle<ast::Expression<'source>>],
4321 span: Span,
4322 ctx: &mut ExpressionContext<'source, '_, '_>,
4323 ) -> Result<'source, ir::Expression> {
4324 let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);
4325
4326 fn get_image_and_span<'source>(
4327 lowerer: &mut Lowerer<'source, '_>,
4328 args: &mut ArgumentContext<'_, 'source>,
4329 ctx: &mut ExpressionContext<'source, '_, '_>,
4330 ) -> Result<'source, (Handle<ir::Expression>, Span)> {
4331 let image = args.next()?;
4332 let image_span = ctx.ast_expressions.get_span(image);
4333 let image = lowerer.expression_for_abstract(image, ctx)?;
4334 Ok((image, image_span))
4335 }
4336
4337 let image;
4338 let image_span;
4339 let gather;
4340 match fun {
4341 Texture::Gather => {
4342 let image_or_component = args.next()?;
4343 let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);
4344 let lowered_image_or_component = self.expression(image_or_component, ctx)?;
4346
4347 match *resolve_inner!(ctx, lowered_image_or_component) {
4348 ir::TypeInner::Image {
4349 class: ir::ImageClass::Depth { .. },
4350 ..
4351 } => {
4352 image = lowered_image_or_component;
4353 image_span = image_or_component_span;
4354 gather = Some(ir::SwizzleComponent::X);
4355 }
4356 _ => {
4357 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4358 gather = Some(ctx.gather_component(
4359 lowered_image_or_component,
4360 image_or_component_span,
4361 span,
4362 )?);
4363 }
4364 }
4365 }
4366 Texture::GatherCompare => {
4367 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4368 gather = Some(ir::SwizzleComponent::X);
4369 }
4370
4371 _ => {
4372 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4373 gather = None;
4374 }
4375 };
4376
4377 let sampler = self.expression_for_abstract(args.next()?, ctx)?;
4378
4379 let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4380 let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge);
4381
4382 let (class, arrayed) = ctx.image_data(image, image_span)?;
4383 let array_index = arrayed
4384 .then(|| self.expression(args.next()?, ctx))
4385 .transpose()?;
4386
4387 let level;
4388 let depth_ref;
4389 match fun {
4390 Texture::Gather => {
4391 level = ir::SampleLevel::Zero;
4392 depth_ref = None;
4393 }
4394 Texture::GatherCompare => {
4395 let reference =
4396 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4397 level = ir::SampleLevel::Zero;
4398 depth_ref = Some(reference);
4399 }
4400
4401 Texture::Sample => {
4402 level = ir::SampleLevel::Auto;
4403 depth_ref = None;
4404 }
4405 Texture::SampleBias => {
4406 let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4407 level = ir::SampleLevel::Bias(bias);
4408 depth_ref = None;
4409 }
4410 Texture::SampleCompare => {
4411 let reference =
4412 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4413 level = ir::SampleLevel::Auto;
4414 depth_ref = Some(reference);
4415 }
4416 Texture::SampleCompareLevel => {
4417 let reference =
4418 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4419 level = ir::SampleLevel::Zero;
4420 depth_ref = Some(reference);
4421 }
4422 Texture::SampleGrad => {
4423 let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4424 let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4425 level = ir::SampleLevel::Gradient { x, y };
4426 depth_ref = None;
4427 }
4428 Texture::SampleLevel => {
4429 let exact = match class {
4430 ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?,
4433
4434 ir::ImageClass::Sampled { .. } => {
4437 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?
4438 }
4439
4440 ir::ImageClass::Storage { .. } | ir::ImageClass::External => {
4444 self.expression(args.next()?, ctx)?
4445 }
4446 };
4447 level = ir::SampleLevel::Exact(exact);
4448 depth_ref = None;
4449 }
4450 Texture::SampleBaseClampToEdge => {
4451 level = crate::SampleLevel::Zero;
4452 depth_ref = None;
4453 }
4454 };
4455
4456 let offset = args
4457 .next()
4458 .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const()))
4459 .ok()
4460 .transpose()?;
4461
4462 args.finish()?;
4463
4464 Ok(ir::Expression::ImageSample {
4465 image,
4466 sampler,
4467 gather,
4468 coordinate,
4469 array_index,
4470 offset,
4471 level,
4472 depth_ref,
4473 clamp_to_edge,
4474 })
4475 }
4476
4477 fn subgroup_operation_helper(
4478 &mut self,
4479 span: Span,
4480 op: ir::SubgroupOperation,
4481 collective_op: ir::CollectiveOperation,
4482 arguments: &[Handle<ast::Expression<'source>>],
4483 ctx: &mut ExpressionContext<'source, '_, '_>,
4484 ) -> Result<'source, Handle<ir::Expression>> {
4485 let mut args = ctx.prepare_args(arguments, 1, span);
4486
4487 let argument = self.expression(args.next()?, ctx)?;
4488 args.finish()?;
4489
4490 let ty = ctx.register_type(argument)?;
4491
4492 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4493 let rctx = ctx.runtime_expression_ctx(span)?;
4494 rctx.block.push(
4495 ir::Statement::SubgroupCollectiveOperation {
4496 op,
4497 collective_op,
4498 argument,
4499 result,
4500 },
4501 span,
4502 );
4503 Ok(result)
4504 }
4505
4506 fn subgroup_gather_helper(
4507 &mut self,
4508 span: Span,
4509 mode: SubgroupGather,
4510 arguments: &[Handle<ast::Expression<'source>>],
4511 ctx: &mut ExpressionContext<'source, '_, '_>,
4512 ) -> Result<'source, Handle<ir::Expression>> {
4513 let mut args = ctx.prepare_args(arguments, 2, span);
4514
4515 let argument = self.expression(args.next()?, ctx)?;
4516
4517 use SubgroupGather as Sg;
4518 let mode = if let Sg::BroadcastFirst = mode {
4519 ir::GatherMode::BroadcastFirst
4520 } else {
4521 let index = self.expression(args.next()?, ctx)?;
4522 match mode {
4523 Sg::BroadcastFirst => unreachable!(),
4524 Sg::Broadcast => ir::GatherMode::Broadcast(index),
4525 Sg::Shuffle => ir::GatherMode::Shuffle(index),
4526 Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index),
4527 Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index),
4528 Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index),
4529 Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index),
4530 }
4531 };
4532
4533 args.finish()?;
4534
4535 let ty = ctx.register_type(argument)?;
4536
4537 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4538 let rctx = ctx.runtime_expression_ctx(span)?;
4539 rctx.block.push(
4540 ir::Statement::SubgroupGather {
4541 mode,
4542 argument,
4543 result,
4544 },
4545 span,
4546 );
4547 Ok(result)
4548 }
4549
4550 fn r#struct(
4551 &mut self,
4552 s: &ast::Struct<'source>,
4553 span: Span,
4554 ctx: &mut GlobalContext<'source, '_, '_>,
4555 ) -> Result<'source, Handle<ir::Type>> {
4556 let mut offset = 0;
4557 let mut struct_alignment = proc::Alignment::ONE;
4558 let mut members = Vec::with_capacity(s.members.len());
4559
4560 let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new();
4561
4562 for member in s.members.iter() {
4563 let ty = self.resolve_ast_type(&member.ty, &mut ctx.as_const())?;
4564
4565 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
4566 let LayoutErrorInner::TooLarge = err.inner else {
4567 unreachable!("unexpected layout error: {err:?}");
4568 };
4569 if ty == err.ty {
4575 Box::new(Error::StructMemberTooLarge {
4576 member_name_span: member.name.span,
4577 })
4578 } else {
4579 Box::new(Error::TypeTooLarge {
4582 span: ctx.module.types.get_span(err.ty),
4583 })
4584 }
4585 })?;
4586
4587 let member_min_size = ctx.layouter[ty].size;
4588 let member_min_alignment = ctx.layouter[ty].alignment;
4589
4590 let member_size = if let Some(size_expr) = member.size {
4591 let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;
4592 if size < member_min_size {
4593 return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size)));
4594 } else {
4595 size
4596 }
4597 } else {
4598 member_min_size
4599 };
4600
4601 let member_alignment = if let Some(align_expr) = member.align {
4602 let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;
4603 if let Some(alignment) = proc::Alignment::new(align) {
4604 if alignment < member_min_alignment {
4605 return Err(Box::new(Error::AlignAttributeTooLow(
4606 span,
4607 member_min_alignment,
4608 )));
4609 } else {
4610 alignment
4611 }
4612 } else {
4613 return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span)));
4614 }
4615 } else {
4616 member_min_alignment
4617 };
4618
4619 let binding = self.binding(&member.binding, ty, ctx)?;
4620
4621 offset = member_alignment.round_up(offset);
4622 struct_alignment = struct_alignment.max(member_alignment);
4623
4624 if !member.doc_comments.is_empty() {
4625 doc_comments.push(Some(
4626 member.doc_comments.iter().map(|s| s.to_string()).collect(),
4627 ));
4628 }
4629 members.push(ir::StructMember {
4630 name: Some(member.name.name.to_owned()),
4631 ty,
4632 binding,
4633 offset,
4634 });
4635
4636 offset += member_size;
4637 if offset > crate::valid::MAX_TYPE_SIZE {
4638 return Err(Box::new(Error::TypeTooLarge { span }));
4639 }
4640 }
4641
4642 let size = struct_alignment.round_up(offset);
4643 let inner = ir::TypeInner::Struct {
4644 members,
4645 span: size,
4646 };
4647
4648 let handle = ctx.module.types.insert(
4649 ir::Type {
4650 name: Some(s.name.name.to_string()),
4651 inner,
4652 },
4653 span,
4654 );
4655 for (i, c) in doc_comments.drain(..).enumerate() {
4656 if let Some(comment) = c {
4657 ctx.module
4658 .get_or_insert_default_doc_comments()
4659 .struct_members
4660 .insert((handle, i), comment);
4661 }
4662 }
4663 Ok(handle)
4664 }
4665
4666 fn const_u32(
4667 &mut self,
4668 expr: Handle<ast::Expression<'source>>,
4669 ctx: &mut ExpressionContext<'source, '_, '_>,
4670 ) -> Result<'source, (u32, Span)> {
4671 let span = ctx.ast_expressions.get_span(expr);
4672 let expr = self.expression(expr, ctx)?;
4673 let value = ctx
4674 .module
4675 .to_ctx()
4676 .get_const_val(expr)
4677 .map_err(|err| match err {
4678 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4679 Error::ExpectedConstExprConcreteIntegerScalar(span)
4680 }
4681 proc::ConstValueError::Negative => Error::ExpectedNonNegative(span),
4682 })?;
4683 Ok((value, span))
4684 }
4685
4686 fn array_size(
4687 &mut self,
4688 expr: Handle<ast::Expression<'source>>,
4689 ctx: &mut ExpressionContext<'source, '_, '_>,
4690 ) -> Result<'source, ir::ArraySize> {
4691 let span = ctx.ast_expressions.get_span(expr);
4692 let const_ctx = &mut ctx.as_const();
4693 let const_expr = self.expression(expr, const_ctx);
4694 match const_expr {
4695 Ok(value) => {
4696 let len = const_ctx.get_const_val(value).map_err(|err| {
4697 Box::new(match err {
4698 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4699 Error::ExpectedConstExprConcreteIntegerScalar(span)
4700 }
4701 proc::ConstValueError::Negative => Error::ExpectedPositiveArrayLength(span),
4702 })
4703 })?;
4704 let size = NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;
4705 Ok(ir::ArraySize::Constant(size))
4706 }
4707 Err(err) => {
4708 let Error::ConstantEvaluatorError(ref ty, _) = *err else {
4711 return Err(err);
4712 };
4713
4714 let proc::ConstantEvaluatorError::OverrideExpr = **ty else {
4715 return Err(err);
4716 };
4717
4718 Ok(ir::ArraySize::Pending(self.array_size_override(
4719 expr,
4720 &mut ctx.as_global().as_override(),
4721 span,
4722 )?))
4723 }
4724 }
4725 }
4726
4727 fn array_size_override(
4728 &mut self,
4729 size_expr: Handle<ast::Expression<'source>>,
4730 ctx: &mut ExpressionContext<'source, '_, '_>,
4731 span: Span,
4732 ) -> Result<'source, Handle<ir::Override>> {
4733 let expr = self.expression(size_expr, ctx)?;
4734 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
4735 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({
4736 if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] {
4737 handle
4738 } else {
4739 let ty = ctx.register_type(expr)?;
4740 ctx.module.overrides.append(
4741 ir::Override {
4742 name: None,
4743 id: None,
4744 ty,
4745 init: Some(expr),
4746 },
4747 span,
4748 )
4749 }
4750 }),
4751 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
4752 span,
4753 ))),
4754 }
4755 }
4756
4757 fn resolve_named_ast_type(
4767 &mut self,
4768 ident: &ast::TemplateElaboratedIdent<'source>,
4769 name: String,
4770 ctx: &mut ExpressionContext<'source, '_, '_>,
4771 ) -> Result<'source, Handle<ir::Type>> {
4772 self.type_specifier(ident, ctx, Some(name))
4773 }
4774
4775 fn resolve_ast_type(
4777 &mut self,
4778 ident: &ast::TemplateElaboratedIdent<'source>,
4779 ctx: &mut ExpressionContext<'source, '_, '_>,
4780 ) -> Result<'source, Handle<ir::Type>> {
4781 self.type_specifier(ident, ctx, None)
4782 }
4783
4784 fn binding(
4785 &mut self,
4786 binding: &Option<ast::Binding<'source>>,
4787 ty: Handle<ir::Type>,
4788 ctx: &mut GlobalContext<'source, '_, '_>,
4789 ) -> Result<'source, Option<ir::Binding>> {
4790 Ok(match *binding {
4791 Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)),
4792 Some(ast::Binding::Location {
4793 location,
4794 interpolation,
4795 sampling,
4796 blend_src,
4797 per_primitive,
4798 }) => {
4799 let blend_src = if let Some(blend_src) = blend_src {
4800 Some(self.const_u32(blend_src, &mut ctx.as_const())?.0)
4801 } else {
4802 None
4803 };
4804
4805 let mut binding = ir::Binding::Location {
4806 location: self.const_u32(location, &mut ctx.as_const())?.0,
4807 interpolation,
4808 sampling,
4809 blend_src,
4810 per_primitive,
4811 };
4812 binding.apply_default_interpolation(&ctx.module.types[ty].inner);
4813 Some(binding)
4814 }
4815 None => None,
4816 })
4817 }
4818
4819 fn ray_query_pointer(
4820 &mut self,
4821 expr: Handle<ast::Expression<'source>>,
4822 ctx: &mut ExpressionContext<'source, '_, '_>,
4823 ) -> Result<'source, Handle<ir::Expression>> {
4824 let span = ctx.ast_expressions.get_span(expr);
4825 let pointer = self.expression(expr, ctx)?;
4826
4827 match *resolve_inner!(ctx, pointer) {
4828 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4829 ir::TypeInner::RayQuery { .. } => Ok(pointer),
4830 ref other => {
4831 log::error!("Pointer type to {other:?} passed to ray query op");
4832 Err(Box::new(Error::InvalidRayQueryPointer(span)))
4833 }
4834 },
4835 ref other => {
4836 log::error!("Type {other:?} passed to ray query op");
4837 Err(Box::new(Error::InvalidRayQueryPointer(span)))
4838 }
4839 }
4840 }
4841}
4842
4843impl ir::AtomicFunction {
4844 pub fn map(word: &str) -> Option<Self> {
4845 Some(match word {
4846 "atomicAdd" => ir::AtomicFunction::Add,
4847 "atomicSub" => ir::AtomicFunction::Subtract,
4848 "atomicAnd" => ir::AtomicFunction::And,
4849 "atomicOr" => ir::AtomicFunction::InclusiveOr,
4850 "atomicXor" => ir::AtomicFunction::ExclusiveOr,
4851 "atomicMin" => ir::AtomicFunction::Min,
4852 "atomicMax" => ir::AtomicFunction::Max,
4853 "atomicExchange" => ir::AtomicFunction::Exchange { compare: None },
4854 _ => return None,
4855 })
4856 }
4857}