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 memory_decorations: v.memory_decorations,
1352 },
1353 span,
1354 );
1355
1356 if !v.doc_comments.is_empty() {
1357 ctx.module
1358 .get_or_insert_default_doc_comments()
1359 .global_variables
1360 .insert(
1361 handle,
1362 v.doc_comments.iter().map(|s| s.to_string()).collect(),
1363 );
1364 }
1365 ctx.globals
1366 .insert(v.name.name, LoweredGlobalDecl::Var(handle));
1367 }
1368 ast::GlobalDeclKind::Const(ref c) => {
1369 let mut ectx = ctx.as_const();
1370
1371 let explicit_ty =
1372 c.ty.as_ref()
1373 .map(|ast| self.resolve_ast_type(ast, &mut ectx))
1374 .transpose()?;
1375
1376 let (ty, init) = self.type_and_init(
1377 c.name,
1378 Some(c.init),
1379 explicit_ty,
1380 AbstractRule::Allow,
1381 &mut ectx,
1382 )?;
1383 let init = init.expect("Global const must have init");
1384
1385 let handle = ctx.module.constants.append(
1386 ir::Constant {
1387 name: Some(c.name.name.to_string()),
1388 ty,
1389 init,
1390 },
1391 span,
1392 );
1393
1394 ctx.globals
1395 .insert(c.name.name, LoweredGlobalDecl::Const(handle));
1396 if !c.doc_comments.is_empty() {
1397 ctx.module
1398 .get_or_insert_default_doc_comments()
1399 .constants
1400 .insert(
1401 handle,
1402 c.doc_comments.iter().map(|s| s.to_string()).collect(),
1403 );
1404 }
1405 }
1406 ast::GlobalDeclKind::Override(ref o) => {
1407 let explicit_ty =
1408 o.ty.as_ref()
1409 .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1410 .transpose()?;
1411
1412 let mut ectx = ctx.as_override();
1413
1414 let (ty, init) = self.type_and_init(
1415 o.name,
1416 o.init,
1417 explicit_ty,
1418 AbstractRule::Concretize,
1419 &mut ectx,
1420 )?;
1421
1422 let id =
1423 o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))
1424 .transpose()?;
1425
1426 let id = if let Some((id, id_span)) = id {
1427 Some(
1428 u16::try_from(id)
1429 .map_err(|_| Error::PipelineConstantIDValue(id_span))?,
1430 )
1431 } else {
1432 None
1433 };
1434
1435 let handle = ctx.module.overrides.append(
1436 ir::Override {
1437 name: Some(o.name.name.to_string()),
1438 id,
1439 ty,
1440 init,
1441 },
1442 span,
1443 );
1444
1445 ctx.globals
1446 .insert(o.name.name, LoweredGlobalDecl::Override(handle));
1447 }
1448 ast::GlobalDeclKind::Struct(ref s) => {
1449 let handle = self.r#struct(s, span, &mut ctx)?;
1450 ctx.globals
1451 .insert(s.name.name, LoweredGlobalDecl::Type(handle));
1452 if !s.doc_comments.is_empty() {
1453 ctx.module
1454 .get_or_insert_default_doc_comments()
1455 .types
1456 .insert(
1457 handle,
1458 s.doc_comments.iter().map(|s| s.to_string()).collect(),
1459 );
1460 }
1461 }
1462 ast::GlobalDeclKind::Type(ref alias) => {
1463 let ty = self.resolve_named_ast_type(
1464 &alias.ty,
1465 alias.name.name.to_string(),
1466 &mut ctx.as_const(),
1467 )?;
1468 ctx.globals
1469 .insert(alias.name.name, LoweredGlobalDecl::Type(ty));
1470 }
1471 ast::GlobalDeclKind::ConstAssert(condition) => {
1472 let condition = self.expression(condition, &mut ctx.as_const())?;
1473
1474 let span = ctx.module.global_expressions.get_span(condition);
1475 match ctx
1476 .module
1477 .to_ctx()
1478 .get_const_val_from(condition, &ctx.module.global_expressions)
1479 {
1480 Ok(true) => Ok(()),
1481 Ok(false) => Err(Error::ConstAssertFailed(span)),
1482 Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
1483 unreachable!()
1484 }
1485 Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
1486 }?;
1487 }
1488 }
1489 }
1490
1491 crate::compact::compact(&mut module, KeepUnused::Yes);
1495
1496 Ok(module)
1497 }
1498
1499 fn type_and_init(
1501 &mut self,
1502 name: ast::Ident<'source>,
1503 init: Option<Handle<ast::Expression<'source>>>,
1504 explicit_ty: Option<Handle<ir::Type>>,
1505 abstract_rule: AbstractRule,
1506 ectx: &mut ExpressionContext<'source, '_, '_>,
1507 ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> {
1508 let ty;
1509 let initializer;
1510 match (init, explicit_ty) {
1511 (Some(init), Some(explicit_ty)) => {
1512 let init = self.expression_for_abstract(init, ectx)?;
1513 let ty_res = proc::TypeResolution::Handle(explicit_ty);
1514 let init = ectx
1515 .try_automatic_conversions(init, &ty_res, name.span)
1516 .map_err(|error| match *error {
1517 Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch {
1518 name: name.span,
1519 expected: e.dest_type,
1520 got: e.source_type,
1521 }),
1522 _ => error,
1523 })?;
1524
1525 let init_ty = ectx.register_type(init)?;
1526 if !ectx.module.compare_types(
1527 &proc::TypeResolution::Handle(explicit_ty),
1528 &proc::TypeResolution::Handle(init_ty),
1529 ) {
1530 return Err(Box::new(Error::InitializationTypeMismatch {
1531 name: name.span,
1532 expected: ectx.type_to_string(explicit_ty),
1533 got: ectx.type_to_string(init_ty),
1534 }));
1535 }
1536 ty = explicit_ty;
1537 initializer = Some(init);
1538 }
1539 (Some(init), None) => {
1540 let mut init = self.expression_for_abstract(init, ectx)?;
1541 if let AbstractRule::Concretize = abstract_rule {
1542 init = ectx.concretize(init)?;
1543 }
1544 ty = ectx.register_type(init)?;
1545 initializer = Some(init);
1546 }
1547 (None, Some(explicit_ty)) => {
1548 ty = explicit_ty;
1549 initializer = None;
1550 }
1551 (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))),
1552 }
1553 Ok((ty, initializer))
1554 }
1555
1556 fn function(
1557 &mut self,
1558 f: &ast::Function<'source>,
1559 span: Span,
1560 ctx: &mut GlobalContext<'source, '_, '_>,
1561 ) -> Result<'source, LoweredGlobalDecl> {
1562 let mut local_table = FastHashMap::default();
1563 let mut expressions = Arena::new();
1564 let mut named_expressions = FastIndexMap::default();
1565 let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new();
1566
1567 let arguments = f
1568 .arguments
1569 .iter()
1570 .enumerate()
1571 .map(|(i, arg)| -> Result<'_, _> {
1572 let ty = self.resolve_ast_type(&arg.ty, &mut ctx.as_const())?;
1573 let expr =
1574 expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span);
1575 local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr)));
1576 named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));
1577 local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime);
1578
1579 Ok(ir::FunctionArgument {
1580 name: Some(arg.name.name.to_string()),
1581 ty,
1582 binding: self.binding(&arg.binding, ty, ctx)?,
1583 })
1584 })
1585 .collect::<Result<Vec<_>>>()?;
1586
1587 let result = f
1588 .result
1589 .as_ref()
1590 .map(|res| -> Result<'_, _> {
1591 let ty = self.resolve_ast_type(&res.ty, &mut ctx.as_const())?;
1592 Ok(ir::FunctionResult {
1593 ty,
1594 binding: self.binding(&res.binding, ty, ctx)?,
1595 })
1596 })
1597 .transpose()?;
1598
1599 let mut function = ir::Function {
1600 name: Some(f.name.name.to_string()),
1601 arguments,
1602 result,
1603 local_variables: Arena::new(),
1604 expressions,
1605 named_expressions: crate::NamedExpressions::default(),
1606 body: ir::Block::default(),
1607 diagnostic_filter_leaf: f.diagnostic_filter_leaf,
1608 };
1609
1610 let mut typifier = Typifier::default();
1611 let mut stmt_ctx = StatementContext {
1612 enable_extensions: ctx.enable_extensions,
1613 local_table: &mut local_table,
1614 globals: ctx.globals,
1615 ast_expressions: ctx.ast_expressions,
1616 const_typifier: ctx.const_typifier,
1617 typifier: &mut typifier,
1618 layouter: ctx.layouter,
1619 function: &mut function,
1620 named_expressions: &mut named_expressions,
1621 module: ctx.module,
1622 local_expression_kind_tracker: &mut local_expression_kind_tracker,
1623 global_expression_kind_tracker: ctx.global_expression_kind_tracker,
1624 };
1625 let mut body = self.block(&f.body, false, &mut stmt_ctx)?;
1626 proc::ensure_block_returns(&mut body);
1627
1628 function.body = body;
1629 function.named_expressions = named_expressions
1630 .into_iter()
1631 .map(|(key, (name, _))| (key, name))
1632 .collect();
1633
1634 if let Some(ref entry) = f.entry_point {
1635 let (workgroup_size, workgroup_size_overrides) =
1636 if let Some(workgroup_size) = entry.workgroup_size {
1637 let mut workgroup_size_out = [1; 3];
1639 let mut workgroup_size_overrides_out = [None; 3];
1640 for (i, size) in workgroup_size.into_iter().enumerate() {
1641 if let Some(size_expr) = size {
1642 match self.const_u32(size_expr, &mut ctx.as_const()) {
1643 Ok(value) => {
1644 workgroup_size_out[i] = value.0;
1645 }
1646 Err(err) => {
1647 if let Error::ConstantEvaluatorError(ref ty, _) = *err {
1648 match **ty {
1649 proc::ConstantEvaluatorError::OverrideExpr => {
1650 workgroup_size_overrides_out[i] =
1651 Some(self.workgroup_size_override(
1652 size_expr,
1653 &mut ctx.as_override(),
1654 )?);
1655 }
1656 _ => {
1657 return Err(err);
1658 }
1659 }
1660 } else {
1661 return Err(err);
1662 }
1663 }
1664 }
1665 }
1666 }
1667 if workgroup_size_overrides_out.iter().all(|x| x.is_none()) {
1668 (workgroup_size_out, None)
1669 } else {
1670 (workgroup_size_out, Some(workgroup_size_overrides_out))
1671 }
1672 } else {
1673 ([0; 3], None)
1674 };
1675
1676 let mesh_info = if let Some((var_name, var_span)) = entry.mesh_output_variable {
1677 let var = match ctx.globals.get(var_name) {
1678 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1679 Some(_) => {
1680 return Err(Box::new(Error::ExpectedGlobalVariable {
1681 name_span: var_span,
1682 }))
1683 }
1684 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1685 };
1686
1687 let mut info = ctx.module.analyze_mesh_shader_info(var);
1688 if let Some(h) = info.1[0] {
1689 info.0.max_vertices_override = Some(
1690 ctx.module
1691 .global_expressions
1692 .append(crate::Expression::Override(h), Span::UNDEFINED),
1693 );
1694 }
1695 if let Some(h) = info.1[1] {
1696 info.0.max_primitives_override = Some(
1697 ctx.module
1698 .global_expressions
1699 .append(crate::Expression::Override(h), Span::UNDEFINED),
1700 );
1701 }
1702
1703 Some(info.0)
1704 } else {
1705 None
1706 };
1707
1708 let task_payload = if let Some((var_name, var_span)) = entry.task_payload {
1709 Some(match ctx.globals.get(var_name) {
1710 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1711 Some(_) => {
1712 return Err(Box::new(Error::ExpectedGlobalVariable {
1713 name_span: var_span,
1714 }))
1715 }
1716 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1717 })
1718 } else {
1719 None
1720 };
1721
1722 let incoming_ray_payload =
1723 if let Some((var_name, var_span)) = entry.ray_incoming_payload {
1724 Some(match ctx.globals.get(var_name) {
1725 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1726 Some(_) => {
1727 return Err(Box::new(Error::ExpectedGlobalVariable {
1728 name_span: var_span,
1729 }))
1730 }
1731 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1732 })
1733 } else {
1734 None
1735 };
1736
1737 ctx.module.entry_points.push(ir::EntryPoint {
1738 name: f.name.name.to_string(),
1739 stage: entry.stage,
1740 early_depth_test: entry.early_depth_test,
1741 workgroup_size,
1742 workgroup_size_overrides,
1743 function,
1744 mesh_info,
1745 task_payload,
1746 incoming_ray_payload,
1747 });
1748 Ok(LoweredGlobalDecl::EntryPoint(
1749 ctx.module.entry_points.len() - 1,
1750 ))
1751 } else {
1752 let handle = ctx.module.functions.append(function, span);
1753 Ok(LoweredGlobalDecl::Function {
1754 handle,
1755 must_use: f.result.as_ref().is_some_and(|res| res.must_use),
1756 })
1757 }
1758 }
1759
1760 fn workgroup_size_override(
1761 &mut self,
1762 size_expr: Handle<ast::Expression<'source>>,
1763 ctx: &mut ExpressionContext<'source, '_, '_>,
1764 ) -> Result<'source, Handle<ir::Expression>> {
1765 let span = ctx.ast_expressions.get_span(size_expr);
1766 let expr = self.expression(size_expr, ctx)?;
1767 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
1768 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr),
1769 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
1770 span,
1771 ))),
1772 }
1773 }
1774
1775 fn block(
1776 &mut self,
1777 b: &ast::Block<'source>,
1778 is_inside_loop: bool,
1779 ctx: &mut StatementContext<'source, '_, '_>,
1780 ) -> Result<'source, ir::Block> {
1781 let mut block = ir::Block::default();
1782
1783 for stmt in b.stmts.iter() {
1784 self.statement(stmt, &mut block, is_inside_loop, ctx)?;
1785 }
1786
1787 Ok(block)
1788 }
1789
1790 fn statement(
1791 &mut self,
1792 stmt: &ast::Statement<'source>,
1793 block: &mut ir::Block,
1794 is_inside_loop: bool,
1795 ctx: &mut StatementContext<'source, '_, '_>,
1796 ) -> Result<'source, ()> {
1797 let out = match stmt.kind {
1798 ast::StatementKind::Block(ref block) => {
1799 let block = self.block(block, is_inside_loop, ctx)?;
1800 ir::Statement::Block(block)
1801 }
1802 ast::StatementKind::LocalDecl(ref decl) => match *decl {
1803 ast::LocalDecl::Let(ref l) => {
1804 let mut emitter = proc::Emitter::default();
1805 emitter.start(&ctx.function.expressions);
1806
1807 let explicit_ty = l
1808 .ty
1809 .as_ref()
1810 .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter)))
1811 .transpose()?;
1812
1813 let mut ectx = ctx.as_expression(block, &mut emitter);
1814
1815 let (ty, initializer) = self.type_and_init(
1816 l.name,
1817 Some(l.init),
1818 explicit_ty,
1819 AbstractRule::Concretize,
1820 &mut ectx,
1821 )?;
1822
1823 if ctx.module.types[ty]
1831 .inner
1832 .is_dynamically_sized(&ctx.module.types)
1833 {
1834 return Err(Box::new(Error::TypeNotConstructible(l.name.span)));
1835 }
1836
1837 let initializer =
1840 initializer.expect("type_and_init did not return an initializer");
1841
1842 ctx.local_expression_kind_tracker
1848 .force_non_const(initializer);
1849
1850 block.extend(emitter.finish(&ctx.function.expressions));
1851 ctx.local_table
1852 .insert(l.handle, Declared::Runtime(Typed::Plain(initializer)));
1853 ctx.named_expressions
1854 .insert(initializer, (l.name.name.to_string(), l.name.span));
1855
1856 return Ok(());
1857 }
1858 ast::LocalDecl::Var(ref v) => {
1859 let mut emitter = proc::Emitter::default();
1860 emitter.start(&ctx.function.expressions);
1861
1862 let explicit_ty =
1863 v.ty.as_ref()
1864 .map(|ast| {
1865 self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter))
1866 })
1867 .transpose()?;
1868
1869 let mut ectx = ctx.as_expression(block, &mut emitter);
1870 let (ty, initializer) = self.type_and_init(
1871 v.name,
1872 v.init,
1873 explicit_ty,
1874 AbstractRule::Concretize,
1875 &mut ectx,
1876 )?;
1877
1878 let (const_initializer, initializer) = {
1879 match initializer {
1880 Some(init) => {
1881 if is_inside_loop
1889 || !ctx.local_expression_kind_tracker.is_const_or_override(init)
1890 {
1891 (None, Some(init))
1892 } else {
1893 (Some(init), None)
1894 }
1895 }
1896 None => (None, None),
1897 }
1898 };
1899
1900 let var = ctx.function.local_variables.append(
1901 ir::LocalVariable {
1902 name: Some(v.name.name.to_string()),
1903 ty,
1904 init: const_initializer,
1905 },
1906 stmt.span,
1907 );
1908
1909 let handle = ctx
1910 .as_expression(block, &mut emitter)
1911 .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?;
1912 block.extend(emitter.finish(&ctx.function.expressions));
1913 ctx.local_table
1914 .insert(v.handle, Declared::Runtime(Typed::Reference(handle)));
1915
1916 match initializer {
1917 Some(initializer) => ir::Statement::Store {
1918 pointer: handle,
1919 value: initializer,
1920 },
1921 None => return Ok(()),
1922 }
1923 }
1924 ast::LocalDecl::Const(ref c) => {
1925 let mut emitter = proc::Emitter::default();
1926 emitter.start(&ctx.function.expressions);
1927
1928 let ectx = &mut ctx.as_const(block, &mut emitter);
1929
1930 let explicit_ty =
1931 c.ty.as_ref()
1932 .map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const()))
1933 .transpose()?;
1934
1935 let (_ty, init) = self.type_and_init(
1936 c.name,
1937 Some(c.init),
1938 explicit_ty,
1939 AbstractRule::Allow,
1940 &mut ectx.as_const(),
1941 )?;
1942 let init = init.expect("Local const must have init");
1943
1944 block.extend(emitter.finish(&ctx.function.expressions));
1945 ctx.local_table
1946 .insert(c.handle, Declared::Const(Typed::Plain(init)));
1947 return Ok(());
1948 }
1949 },
1950 ast::StatementKind::If {
1951 condition,
1952 ref accept,
1953 ref reject,
1954 } => {
1955 let mut emitter = proc::Emitter::default();
1956 emitter.start(&ctx.function.expressions);
1957
1958 let condition =
1959 self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;
1960 block.extend(emitter.finish(&ctx.function.expressions));
1961
1962 let accept = self.block(accept, is_inside_loop, ctx)?;
1963 let reject = self.block(reject, is_inside_loop, ctx)?;
1964
1965 ir::Statement::If {
1966 condition,
1967 accept,
1968 reject,
1969 }
1970 }
1971 ast::StatementKind::Switch {
1972 selector,
1973 ref cases,
1974 } => {
1975 let mut emitter = proc::Emitter::default();
1976 emitter.start(&ctx.function.expressions);
1977
1978 let mut ectx = ctx.as_expression(block, &mut emitter);
1979
1980 let (mut exprs, spans) = core::iter::once(selector)
1983 .chain(cases.iter().filter_map(|case| match case.value {
1984 ast::SwitchValue::Expr(expr) => Some(expr),
1985 ast::SwitchValue::Default => None,
1986 }))
1987 .enumerate()
1988 .map(|(i, expr)| {
1989 let span = ectx.ast_expressions.get_span(expr);
1990 let expr = self.expression_for_abstract(expr, &mut ectx)?;
1991 let ty = resolve_inner!(ectx, expr);
1992 match *ty {
1993 ir::TypeInner::Scalar(
1994 ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT,
1995 ) => Ok((expr, span)),
1996 _ => match i {
1997 0 => Err(Box::new(Error::InvalidSwitchSelector { span })),
1998 _ => Err(Box::new(Error::InvalidSwitchCase { span })),
1999 },
2000 }
2001 })
2002 .collect::<Result<(Vec<_>, Vec<_>)>>()?;
2003
2004 let mut consensus =
2005 ectx.automatic_conversion_consensus(None, &exprs)
2006 .map_err(|span_idx| Error::SwitchCaseTypeMismatch {
2007 span: spans[span_idx],
2008 })?;
2009 if consensus == ir::Scalar::ABSTRACT_INT {
2011 consensus = ir::Scalar::I32;
2012 }
2013 for expr in &mut exprs {
2014 ectx.convert_to_leaf_scalar(expr, consensus)?;
2015 }
2016
2017 block.extend(emitter.finish(&ctx.function.expressions));
2018
2019 let mut exprs = exprs.into_iter();
2020 let selector = exprs
2021 .next()
2022 .expect("First element should be selector expression");
2023
2024 let cases = cases
2025 .iter()
2026 .map(|case| {
2027 Ok(ir::SwitchCase {
2028 value: match case.value {
2029 ast::SwitchValue::Expr(expr) => {
2030 let span = ctx.ast_expressions.get_span(expr);
2031 let expr = exprs.next().expect(
2032 "Should yield expression for each SwitchValue::Expr case",
2033 );
2034 match ctx
2035 .module
2036 .to_ctx()
2037 .get_const_val_from(expr, &ctx.function.expressions)
2038 {
2039 Ok(ir::Literal::I32(value)) => ir::SwitchValue::I32(value),
2040 Ok(ir::Literal::U32(value)) => ir::SwitchValue::U32(value),
2041 _ => {
2042 return Err(Box::new(Error::InvalidSwitchCase {
2043 span,
2044 }));
2045 }
2046 }
2047 }
2048 ast::SwitchValue::Default => ir::SwitchValue::Default,
2049 },
2050 body: self.block(&case.body, is_inside_loop, ctx)?,
2051 fall_through: case.fall_through,
2052 })
2053 })
2054 .collect::<Result<_>>()?;
2055
2056 ir::Statement::Switch { selector, cases }
2057 }
2058 ast::StatementKind::Loop {
2059 ref body,
2060 ref continuing,
2061 break_if,
2062 } => {
2063 let body = self.block(body, true, ctx)?;
2064 let mut continuing = self.block(continuing, true, ctx)?;
2065
2066 let mut emitter = proc::Emitter::default();
2067 emitter.start(&ctx.function.expressions);
2068 let break_if = break_if
2069 .map(|expr| {
2070 self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))
2071 })
2072 .transpose()?;
2073 continuing.extend(emitter.finish(&ctx.function.expressions));
2074
2075 ir::Statement::Loop {
2076 body,
2077 continuing,
2078 break_if,
2079 }
2080 }
2081 ast::StatementKind::Break => ir::Statement::Break,
2082 ast::StatementKind::Continue => ir::Statement::Continue,
2083 ast::StatementKind::Return { value: ast_value } => {
2084 let mut emitter = proc::Emitter::default();
2085 emitter.start(&ctx.function.expressions);
2086
2087 let value;
2088 if let Some(ast_expr) = ast_value {
2089 let result_ty = ctx.function.result.as_ref().map(|r| r.ty);
2090 let mut ectx = ctx.as_expression(block, &mut emitter);
2091 let expr = self.expression_for_abstract(ast_expr, &mut ectx)?;
2092
2093 if let Some(result_ty) = result_ty {
2094 let mut ectx = ctx.as_expression(block, &mut emitter);
2095 let resolution = proc::TypeResolution::Handle(result_ty);
2096 let converted =
2097 ectx.try_automatic_conversions(expr, &resolution, Span::default())?;
2098 value = Some(converted);
2099 } else {
2100 value = Some(expr);
2101 }
2102 } else {
2103 value = None;
2104 }
2105 block.extend(emitter.finish(&ctx.function.expressions));
2106
2107 ir::Statement::Return { value }
2108 }
2109 ast::StatementKind::Kill => ir::Statement::Kill,
2110 ast::StatementKind::Call(ref call_phrase) => {
2111 let mut emitter = proc::Emitter::default();
2112 emitter.start(&ctx.function.expressions);
2113
2114 let _ = self.call(
2115 call_phrase,
2116 stmt.span,
2117 &mut ctx.as_expression(block, &mut emitter),
2118 true,
2119 )?;
2120 block.extend(emitter.finish(&ctx.function.expressions));
2121 return Ok(());
2122 }
2123 ast::StatementKind::Assign {
2124 target: ast_target,
2125 op,
2126 value,
2127 } => {
2128 let mut emitter = proc::Emitter::default();
2129 emitter.start(&ctx.function.expressions);
2130 let target_span = ctx.ast_expressions.get_span(ast_target);
2131
2132 let mut ectx = ctx.as_expression(block, &mut emitter);
2133 let target = self.expression_for_reference(ast_target, &mut ectx)?;
2134 let target_handle = match target {
2135 Typed::Reference(handle) => handle,
2136 Typed::Plain(handle) => {
2137 let ty = ctx.invalid_assignment_type(handle);
2138 return Err(Box::new(Error::InvalidAssignment {
2139 span: target_span,
2140 ty,
2141 }));
2142 }
2143 };
2144
2145 let target_scalar = match op {
2150 Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => {
2151 Some(ir::Scalar::U32)
2152 }
2153 _ => resolve_inner!(ectx, target_handle)
2154 .pointer_automatically_convertible_scalar(&ectx.module.types),
2155 };
2156
2157 let op_assign = if let Some(op) = op {
2159 Some((op, ectx.apply_load_rule(target)?))
2160 } else {
2161 None
2162 };
2163
2164 let value = self.expression_for_abstract(value, &mut ectx)?;
2165 let mut value = match target_scalar {
2166 Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar(
2167 value,
2168 target_scalar,
2169 target_span,
2170 )?,
2171 None => value,
2172 };
2173
2174 let value = match op_assign {
2175 Some((op, mut left)) => {
2176 ectx.binary_op_splat(op, &mut left, &mut value)?;
2177 ectx.append_expression(
2178 ir::Expression::Binary {
2179 op,
2180 left,
2181 right: value,
2182 },
2183 stmt.span,
2184 )?
2185 }
2186 None => value,
2187 };
2188 block.extend(emitter.finish(&ctx.function.expressions));
2189
2190 ir::Statement::Store {
2191 pointer: target_handle,
2192 value,
2193 }
2194 }
2195 ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {
2196 let mut emitter = proc::Emitter::default();
2197 emitter.start(&ctx.function.expressions);
2198
2199 let op = match stmt.kind {
2200 ast::StatementKind::Increment(_) => ir::BinaryOperator::Add,
2201 ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract,
2202 _ => unreachable!(),
2203 };
2204
2205 let value_span = ctx.ast_expressions.get_span(value);
2206 let target = self
2207 .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;
2208 let target_handle = target.ref_or(Error::BadIncrDecrReferenceType(value_span))?;
2209
2210 let mut ectx = ctx.as_expression(block, &mut emitter);
2211 let scalar = match *resolve_inner!(ectx, target_handle) {
2212 ir::TypeInner::ValuePointer {
2213 size: None, scalar, ..
2214 } => scalar,
2215 ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {
2216 ir::TypeInner::Scalar(scalar) => scalar,
2217 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2218 },
2219 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2220 };
2221 let literal = match scalar.kind {
2222 ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar)
2223 .ok_or(Error::BadIncrDecrReferenceType(value_span))?,
2224 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2225 };
2226
2227 let right =
2228 ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?;
2229 let rctx = ectx.runtime_expression_ctx(stmt.span)?;
2230 let left = rctx.function.expressions.append(
2231 ir::Expression::Load {
2232 pointer: target_handle,
2233 },
2234 value_span,
2235 );
2236 let value = rctx
2237 .function
2238 .expressions
2239 .append(ir::Expression::Binary { op, left, right }, stmt.span);
2240 rctx.local_expression_kind_tracker
2241 .insert(left, proc::ExpressionKind::Runtime);
2242 rctx.local_expression_kind_tracker
2243 .insert(value, proc::ExpressionKind::Runtime);
2244
2245 block.extend(emitter.finish(&ctx.function.expressions));
2246 ir::Statement::Store {
2247 pointer: target_handle,
2248 value,
2249 }
2250 }
2251 ast::StatementKind::ConstAssert(condition) => {
2252 let mut emitter = proc::Emitter::default();
2253 emitter.start(&ctx.function.expressions);
2254
2255 let condition =
2256 self.expression(condition, &mut ctx.as_const(block, &mut emitter))?;
2257
2258 let span = ctx.function.expressions.get_span(condition);
2259 match ctx
2260 .module
2261 .to_ctx()
2262 .get_const_val_from(condition, &ctx.function.expressions)
2263 {
2264 Ok(true) => Ok(()),
2265 Ok(false) => Err(Error::ConstAssertFailed(span)),
2266 Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
2267 unreachable!()
2268 }
2269 Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
2270 }?;
2271
2272 block.extend(emitter.finish(&ctx.function.expressions));
2273
2274 return Ok(());
2275 }
2276 ast::StatementKind::Phony(expr) => {
2277 let mut emitter = proc::Emitter::default();
2281 emitter.start(&ctx.function.expressions);
2282
2283 let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;
2284 block.extend(emitter.finish(&ctx.function.expressions));
2285 ctx.named_expressions
2286 .insert(value, ("phony".to_string(), stmt.span));
2287 return Ok(());
2288 }
2289 };
2290
2291 block.push(out, stmt.span);
2292
2293 Ok(())
2294 }
2295
2296 fn expression(
2304 &mut self,
2305 expr: Handle<ast::Expression<'source>>,
2306 ctx: &mut ExpressionContext<'source, '_, '_>,
2307 ) -> Result<'source, Handle<ir::Expression>> {
2308 let expr = self.expression_for_abstract(expr, ctx)?;
2309 ctx.concretize(expr)
2310 }
2311
2312 fn expression_for_abstract(
2313 &mut self,
2314 expr: Handle<ast::Expression<'source>>,
2315 ctx: &mut ExpressionContext<'source, '_, '_>,
2316 ) -> Result<'source, Handle<ir::Expression>> {
2317 let expr = self.expression_for_reference(expr, ctx)?;
2318 ctx.apply_load_rule(expr)
2319 }
2320
2321 fn expression_with_leaf_scalar(
2322 &mut self,
2323 expr: Handle<ast::Expression<'source>>,
2324 scalar: ir::Scalar,
2325 ctx: &mut ExpressionContext<'source, '_, '_>,
2326 ) -> Result<'source, Handle<ir::Expression>> {
2327 let unconverted = self.expression_for_abstract(expr, ctx)?;
2328 ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default())
2329 }
2330
2331 fn expression_for_reference(
2332 &mut self,
2333 expr: Handle<ast::Expression<'source>>,
2334 ctx: &mut ExpressionContext<'source, '_, '_>,
2335 ) -> Result<'source, Typed<Handle<ir::Expression>>> {
2336 let span = ctx.ast_expressions.get_span(expr);
2337 let expr = &ctx.ast_expressions[expr];
2338
2339 let expr: Typed<ir::Expression> = match *expr {
2340 ast::Expression::Literal(literal) => {
2341 let literal = match literal {
2342 ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f),
2343 ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f),
2344 ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i),
2345 ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u),
2346 ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i),
2347 ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u),
2348 ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f),
2349 ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i),
2350 ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f),
2351 ast::Literal::Bool(b) => ir::Literal::Bool(b),
2352 };
2353 let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?;
2354 return Ok(Typed::Plain(handle));
2355 }
2356 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2357 ref template_list, ..
2358 }) if !template_list.is_empty() => {
2359 return Err(Box::new(Error::UnexpectedTemplate(span)))
2360 }
2361 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2362 ident: ast::IdentExpr::Local(local),
2363 ..
2364 }) => {
2365 return ctx.local(&local, span);
2366 }
2367 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2368 ident: ast::IdentExpr::Unresolved(name),
2369 ..
2370 }) => {
2371 let global = ctx
2372 .globals
2373 .get(name)
2374 .ok_or(Error::UnknownIdent(span, name))?;
2375 let expr = match *global {
2376 LoweredGlobalDecl::Var(handle) => {
2377 let expr = ir::Expression::GlobalVariable(handle);
2378 let v = &ctx.module.global_variables[handle];
2379 match v.space {
2380 ir::AddressSpace::Handle => Typed::Plain(expr),
2381 _ => Typed::Reference(expr),
2382 }
2383 }
2384 LoweredGlobalDecl::Const(handle) => {
2385 Typed::Plain(ir::Expression::Constant(handle))
2386 }
2387 LoweredGlobalDecl::Override(handle) => {
2388 Typed::Plain(ir::Expression::Override(handle))
2389 }
2390 LoweredGlobalDecl::Function { .. }
2391 | LoweredGlobalDecl::Type(_)
2392 | LoweredGlobalDecl::EntryPoint(_) => {
2393 return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable)));
2394 }
2395 };
2396
2397 return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));
2398 }
2399 ast::Expression::Unary { op, expr } => {
2400 let expr = self.expression_for_abstract(expr, ctx)?;
2401 Typed::Plain(ir::Expression::Unary { op, expr })
2402 }
2403 ast::Expression::AddrOf(expr) => {
2404 match self.expression_for_reference(expr, ctx)? {
2407 Typed::Reference(handle) => {
2408 let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle];
2409 if let &ir::Expression::Access { base, .. }
2410 | &ir::Expression::AccessIndex { base, .. } = expr
2411 {
2412 if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() {
2413 if matches!(
2414 *ty.inner_with(&ctx.module.types),
2415 ir::TypeInner::Vector { .. },
2416 ) {
2417 return Err(Box::new(Error::InvalidAddrOfOperand(
2418 ctx.get_expression_span(handle),
2419 )));
2420 }
2421 }
2422 }
2423 return Ok(Typed::Plain(handle));
2425 }
2426 Typed::Plain(_) => {
2427 return Err(Box::new(Error::NotReference(
2428 "the operand of the `&` operator",
2429 span,
2430 )));
2431 }
2432 }
2433 }
2434 ast::Expression::Deref(expr) => {
2435 let pointer = self.expression(expr, ctx)?;
2437
2438 if resolve_inner!(ctx, pointer).pointer_space().is_none() {
2439 return Err(Box::new(Error::NotPointer(span)));
2440 }
2441
2442 return Ok(Typed::Reference(pointer));
2444 }
2445 ast::Expression::Binary { op, left, right } => {
2446 self.binary(op, left, right, span, ctx)?
2447 }
2448 ast::Expression::Call(ref call_phrase) => {
2449 let handle = self
2450 .call(call_phrase, span, ctx, false)?
2451 .ok_or(Error::FunctionReturnsVoid(span))?;
2452 return Ok(Typed::Plain(handle));
2453 }
2454 ast::Expression::Index { base, index } => {
2455 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2456 let index = self.expression(index, ctx)?;
2457
2458 if let Typed::Plain(handle) = lowered_base {
2461 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2462 lowered_base = Typed::Reference(handle);
2463 }
2464 }
2465
2466 lowered_base.try_map(|base| match ctx.get_const_val(index).ok() {
2467 Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }),
2468 None => {
2469 let base = ctx.concretize(base)?;
2475 Ok(ir::Expression::Access { base, index })
2476 }
2477 })?
2478 }
2479 ast::Expression::Member { base, ref field } => {
2480 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2481
2482 if let Typed::Plain(handle) = lowered_base {
2485 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2486 lowered_base = Typed::Reference(handle);
2487 }
2488 }
2489
2490 let temp_ty;
2491 let composite_type: &ir::TypeInner = match lowered_base {
2492 Typed::Reference(handle) => {
2493 temp_ty = resolve_inner!(ctx, handle)
2494 .pointer_base_type()
2495 .expect("In Typed::Reference(handle), handle must be a Naga pointer");
2496 temp_ty.inner_with(&ctx.module.types)
2497 }
2498
2499 Typed::Plain(handle) => {
2500 resolve_inner!(ctx, handle)
2501 }
2502 };
2503
2504 let access = match *composite_type {
2505 ir::TypeInner::Struct { ref members, .. } => {
2506 let index = members
2507 .iter()
2508 .position(|m| m.name.as_deref() == Some(field.name))
2509 .ok_or(Error::BadAccessor(field.span))?
2510 as u32;
2511
2512 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2513 }
2514 ir::TypeInner::Vector { size: vec_size, .. } => {
2515 match Components::new(field.name, field.span)? {
2516 Components::Swizzle { size, pattern } => {
2517 for &component in pattern[..size as usize].iter() {
2518 if component as u8 >= vec_size as u8 {
2519 return Err(Box::new(Error::BadAccessor(field.span)));
2520 }
2521 }
2522 Typed::Plain(ir::Expression::Swizzle {
2523 size,
2524 vector: ctx.apply_load_rule(lowered_base)?,
2525 pattern,
2526 })
2527 }
2528 Components::Single(index) => {
2529 if index >= vec_size as u32 {
2530 return Err(Box::new(Error::BadAccessor(field.span)));
2531 }
2532 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2533 }
2534 }
2535 }
2536 _ => return Err(Box::new(Error::BadAccessor(field.span))),
2537 };
2538
2539 access
2540 }
2541 };
2542
2543 expr.try_map(|handle| ctx.append_expression(handle, span))
2544 }
2545
2546 fn logical(
2550 &mut self,
2551 op: crate::BinaryOperator,
2552 left: Handle<crate::Expression>,
2553 right: Handle<ast::Expression<'source>>,
2554 span: Span,
2555 ctx: &mut ExpressionContext<'source, '_, '_>,
2556 ) -> Result<'source, Typed<crate::Expression>> {
2557 debug_assert!(
2558 op == crate::BinaryOperator::LogicalAnd || op == crate::BinaryOperator::LogicalOr
2559 );
2560
2561 if ctx.is_runtime() {
2562 let (condition, else_val) = if op == crate::BinaryOperator::LogicalAnd {
2574 let condition = left;
2575 let else_val = ctx.append_expression(
2576 crate::Expression::Literal(crate::Literal::Bool(false)),
2577 span,
2578 )?;
2579 (condition, else_val)
2580 } else {
2581 let condition = ctx.append_expression(
2582 crate::Expression::Unary {
2583 op: crate::UnaryOperator::LogicalNot,
2584 expr: left,
2585 },
2586 span,
2587 )?;
2588 let else_val = ctx.append_expression(
2589 crate::Expression::Literal(crate::Literal::Bool(true)),
2590 span,
2591 )?;
2592 (condition, else_val)
2593 };
2594
2595 let bool_ty = ctx.ensure_type_exists(crate::TypeInner::Scalar(crate::Scalar::BOOL));
2596
2597 let rctx = ctx.runtime_expression_ctx(span)?;
2598 let result_var = rctx.function.local_variables.append(
2599 crate::LocalVariable {
2600 name: None,
2601 ty: bool_ty,
2602 init: None,
2603 },
2604 span,
2605 );
2606 let pointer =
2607 ctx.append_expression(crate::Expression::LocalVariable(result_var), span)?;
2608
2609 let (right, mut accept) = ctx.with_nested_runtime_expression_ctx(span, |ctx| {
2610 let right = self.expression_for_abstract(right, ctx)?;
2611 ctx.grow_types(right)?;
2612 Ok(right)
2613 })?;
2614
2615 accept.push(
2616 crate::Statement::Store {
2617 pointer,
2618 value: right,
2619 },
2620 span,
2621 );
2622
2623 let mut reject = crate::Block::with_capacity(1);
2624 reject.push(
2625 crate::Statement::Store {
2626 pointer,
2627 value: else_val,
2628 },
2629 span,
2630 );
2631
2632 let rctx = ctx.runtime_expression_ctx(span)?;
2633 rctx.block.push(
2634 crate::Statement::If {
2635 condition,
2636 accept,
2637 reject,
2638 },
2639 span,
2640 );
2641
2642 Ok(Typed::Reference(crate::Expression::LocalVariable(
2643 result_var,
2644 )))
2645 } else {
2646 let left_val: Option<bool> = ctx.get_const_val(left).ok();
2647
2648 if left_val.is_some_and(|left_val| {
2649 op == crate::BinaryOperator::LogicalAnd && !left_val
2650 || op == crate::BinaryOperator::LogicalOr && left_val
2651 }) {
2652 Ok(Typed::Plain(ctx.get(left).clone()))
2661 } else {
2662 let right = self.expression_for_abstract(right, ctx)?;
2668 ctx.grow_types(right)?;
2669
2670 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2671 }
2672 }
2673 }
2674
2675 fn type_expression(
2676 &mut self,
2677 expr: Handle<ast::Expression<'source>>,
2678 ctx: &mut ExpressionContext<'source, '_, '_>,
2679 ) -> Result<'source, Handle<ir::Type>> {
2680 let span = ctx.ast_expressions.get_span(expr);
2681 let expr = &ctx.ast_expressions[expr];
2682
2683 let ident = match *expr {
2684 ast::Expression::Ident(ref ident) => ident,
2685 _ => return Err(Box::new(Error::UnexpectedExprForTypeExpression(span))),
2686 };
2687
2688 self.type_specifier(ident, ctx, None)
2689 }
2690
2691 fn type_specifier(
2692 &mut self,
2693 ident: &ast::TemplateElaboratedIdent<'source>,
2694 ctx: &mut ExpressionContext<'source, '_, '_>,
2695 alias_name: Option<String>,
2696 ) -> Result<'source, Handle<ir::Type>> {
2697 let &ast::TemplateElaboratedIdent {
2698 ref ident,
2699 ident_span,
2700 ref template_list,
2701 ..
2702 } = ident;
2703
2704 let ident = match *ident {
2705 ast::IdentExpr::Unresolved(ident) => ident,
2706 ast::IdentExpr::Local(_) => {
2707 return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2710 }
2711 };
2712
2713 let mut tl = TemplateListIter::new(ident_span, template_list);
2714
2715 if let Some(global) = ctx.globals.get(ident) {
2716 let &LoweredGlobalDecl::Type(handle) = global else {
2717 return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2718 };
2719
2720 tl.finish(ctx)?;
2724 return Ok(handle);
2725 }
2726
2727 let ty = conv::map_predeclared_type(&ctx.enable_extensions, ident_span, ident)?
2730 .ok_or_else(|| Box::new(Error::UnknownIdent(ident_span, ident)))?;
2731 let ty = self.finalize_type(ctx, ty, &mut tl, alias_name)?;
2732
2733 tl.finish(ctx)?;
2734
2735 Ok(ty)
2736 }
2737
2738 fn finalize_type(
2756 &mut self,
2757 ctx: &mut ExpressionContext<'source, '_, '_>,
2758 ty: conv::PredeclaredType,
2759 tl: &mut TemplateListIter<'_, 'source>,
2760 alias_name: Option<String>,
2761 ) -> Result<'source, Handle<ir::Type>> {
2762 let ty = match ty {
2763 conv::PredeclaredType::TypeInner(ty_inner) => {
2764 if let ir::TypeInner::Image {
2765 class: ir::ImageClass::External,
2766 ..
2767 } = ty_inner
2768 {
2769 ctx.module.generate_external_texture_types();
2784 }
2785
2786 ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2787 }
2788 conv::PredeclaredType::RayDesc => ctx.module.generate_ray_desc_type(),
2789 conv::PredeclaredType::RayIntersection => ctx.module.generate_ray_intersection_type(),
2790 conv::PredeclaredType::TypeGenerator(type_generator) => {
2791 let ty_inner = match type_generator {
2792 conv::TypeGenerator::Vector { size } => {
2793 let (scalar, _) = tl.scalar_ty(self, ctx)?;
2794 ir::TypeInner::Vector { size, scalar }
2795 }
2796 conv::TypeGenerator::Matrix { columns, rows } => {
2797 let (scalar, span) = tl.scalar_ty(self, ctx)?;
2798 if scalar.kind != ir::ScalarKind::Float {
2799 return Err(Box::new(Error::BadMatrixScalarKind(span, scalar)));
2800 }
2801 ir::TypeInner::Matrix {
2802 columns,
2803 rows,
2804 scalar,
2805 }
2806 }
2807 conv::TypeGenerator::Array => {
2808 let base = tl.ty(self, ctx)?;
2809 let size = tl.maybe_array_size(self, ctx)?;
2810
2811 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
2813 let LayoutErrorInner::TooLarge = err.inner else {
2814 unreachable!("unexpected layout error: {err:?}");
2815 };
2816 Box::new(Error::TypeTooLarge {
2819 span: ctx.module.types.get_span(err.ty),
2820 })
2821 })?;
2822 let stride = ctx.layouter[base].to_stride();
2823
2824 ir::TypeInner::Array { base, size, stride }
2825 }
2826 conv::TypeGenerator::Atomic => {
2827 let (scalar, _) = tl.scalar_ty(self, ctx)?;
2828 ir::TypeInner::Atomic(scalar)
2829 }
2830 conv::TypeGenerator::Pointer => {
2831 let mut space = tl.address_space(ctx)?;
2832 let base = tl.ty(self, ctx)?;
2833 tl.maybe_access_mode(&mut space, ctx)?;
2834 ir::TypeInner::Pointer { base, space }
2835 }
2836 conv::TypeGenerator::SampledTexture {
2837 dim,
2838 arrayed,
2839 multi,
2840 } => {
2841 let (scalar, span) = tl.scalar_ty(self, ctx)?;
2842 let ir::Scalar { kind, width } = scalar;
2843 if width != 4 {
2844 return Err(Box::new(Error::BadTextureSampleType { span, scalar }));
2845 }
2846 ir::TypeInner::Image {
2847 dim,
2848 arrayed,
2849 class: ir::ImageClass::Sampled { kind, multi },
2850 }
2851 }
2852 conv::TypeGenerator::StorageTexture { dim, arrayed } => {
2853 let format = tl.storage_format(ctx)?;
2854 let access = tl.access_mode(ctx)?;
2855 ir::TypeInner::Image {
2856 dim,
2857 arrayed,
2858 class: ir::ImageClass::Storage { format, access },
2859 }
2860 }
2861 conv::TypeGenerator::BindingArray => {
2862 let base = tl.ty(self, ctx)?;
2863 let size = tl.maybe_array_size(self, ctx)?;
2864 ir::TypeInner::BindingArray { base, size }
2865 }
2866 conv::TypeGenerator::AccelerationStructure => {
2867 let vertex_return = tl.maybe_vertex_return(ctx)?;
2868 ir::TypeInner::AccelerationStructure { vertex_return }
2869 }
2870 conv::TypeGenerator::RayQuery => {
2871 let vertex_return = tl.maybe_vertex_return(ctx)?;
2872 ir::TypeInner::RayQuery { vertex_return }
2873 }
2874 conv::TypeGenerator::CooperativeMatrix { columns, rows } => {
2875 let (ty, span) = tl.ty_with_span(self, ctx)?;
2876 let ir::TypeInner::Scalar(scalar) = ctx.module.types[ty].inner else {
2877 return Err(Box::new(Error::UnsupportedCooperativeScalar(span)));
2878 };
2879 let role = tl.cooperative_role(ctx)?;
2880 ir::TypeInner::CooperativeMatrix {
2881 columns,
2882 rows,
2883 scalar,
2884 role,
2885 }
2886 }
2887 };
2888 ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2889 }
2890 };
2891 Ok(ty)
2892 }
2893
2894 fn binary(
2895 &mut self,
2896 op: ir::BinaryOperator,
2897 left: Handle<ast::Expression<'source>>,
2898 right: Handle<ast::Expression<'source>>,
2899 span: Span,
2900 ctx: &mut ExpressionContext<'source, '_, '_>,
2901 ) -> Result<'source, Typed<ir::Expression>> {
2902 if op == ir::BinaryOperator::LogicalAnd || op == ir::BinaryOperator::LogicalOr {
2903 let left = self.expression_for_abstract(left, ctx)?;
2904 ctx.grow_types(left)?;
2905
2906 if !matches!(
2907 resolve_inner!(ctx, left),
2908 &ir::TypeInner::Scalar(ir::Scalar::BOOL)
2909 ) {
2910 let right = self.expression_for_abstract(right, ctx)?;
2912 ctx.grow_types(right)?;
2913 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2914 } else {
2915 self.logical(op, left, right, span, ctx)
2916 }
2917 } else {
2918 let mut left = self.expression_for_abstract(left, ctx)?;
2920 let mut right = self.expression_for_abstract(right, ctx)?;
2921
2922 ctx.binary_op_splat(op, &mut left, &mut right)?;
2925
2926 match op {
2928 ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => {
2929 right =
2934 ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?;
2935
2936 if !ctx.is_const(right) {
2948 left = ctx.concretize(left)?;
2949 }
2950 }
2951
2952 _ => {
2957 ctx.grow_types(left)?;
2958 ctx.grow_types(right)?;
2959 if let Ok(consensus_scalar) =
2960 ctx.automatic_conversion_consensus(None, [left, right].iter())
2961 {
2962 ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;
2963 ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;
2964 }
2965 }
2966 }
2967
2968 Ok(Typed::Plain(ir::Expression::Binary { op, left, right }))
2969 }
2970 }
2971
2972 #[allow(clippy::too_many_arguments)]
2974 fn call_builtin<'phrase>(
2975 &mut self,
2976 function_name: &'source str,
2977 function_span: Span,
2978 arguments: &[Handle<ast::Expression<'source>>],
2979 template_params: &mut TemplateListIter<'phrase, 'source>,
2980 call_span: Span,
2981 ctx: &mut ExpressionContext<'source, '_, '_>,
2982 is_statement: bool,
2983 ) -> Result<'source, Option<(Handle<ir::Expression>, MustUse)>> {
2984 let (expr, must_use) = if let Some(fun) = conv::map_relational_fun(function_name) {
2985 let mut args = ctx.prepare_args(arguments, 1, function_span);
2986 let argument = self.expression(args.next()?, ctx)?;
2987 args.finish()?;
2988
2989 let argument_unmodified = matches!(
2991 fun,
2992 ir::RelationalFunction::All | ir::RelationalFunction::Any
2993 ) && {
2994 matches!(
2995 resolve_inner!(ctx, argument),
2996 &ir::TypeInner::Scalar(ir::Scalar {
2997 kind: ir::ScalarKind::Bool,
2998 ..
2999 })
3000 )
3001 };
3002
3003 if argument_unmodified {
3004 return Ok(Some((argument, MustUse::Yes)));
3005 } else {
3006 (ir::Expression::Relational { fun, argument }, MustUse::Yes)
3007 }
3008 } else if let Some((axis, ctrl)) = conv::map_derivative(function_name) {
3009 let mut args = ctx.prepare_args(arguments, 1, function_span);
3010 let expr = self.expression(args.next()?, ctx)?;
3011 args.finish()?;
3012
3013 (
3014 ir::Expression::Derivative { axis, ctrl, expr },
3015 MustUse::Yes,
3016 )
3017 } else if let Some(fun) = conv::map_standard_fun(function_name) {
3018 (
3019 self.math_function_helper(function_span, fun, arguments, ctx)?,
3020 MustUse::Yes,
3021 )
3022 } else if let Some(fun) = Texture::map(function_name) {
3023 (
3024 self.texture_sample_helper(fun, arguments, function_span, ctx)?,
3025 MustUse::Yes,
3026 )
3027 } else if let Some((op, cop)) = conv::map_subgroup_operation(function_name) {
3028 return Ok(Some((
3029 self.subgroup_operation_helper(function_span, op, cop, arguments, ctx)?,
3030 MustUse::Yes,
3031 )));
3032 } else if let Some(mode) = SubgroupGather::map(function_name) {
3033 return Ok(Some((
3034 self.subgroup_gather_helper(function_span, mode, arguments, ctx)?,
3035 MustUse::Yes,
3036 )));
3037 } else if let Some(fun) = ir::AtomicFunction::map(function_name) {
3038 return Ok(self
3039 .atomic_helper(function_span, fun, arguments, is_statement, ctx)?
3040 .map(|result| (result, MustUse::No)));
3041 } else {
3042 match function_name {
3043 "bitcast" => {
3044 let ty = template_params.ty(self, ctx)?;
3045
3046 let mut args = ctx.prepare_args(arguments, 1, function_span);
3047 let expr = self.expression(args.next()?, ctx)?;
3048 args.finish()?;
3049
3050 let element_scalar = match ctx.module.types[ty].inner {
3051 ir::TypeInner::Scalar(scalar) => scalar,
3052 ir::TypeInner::Vector { scalar, .. } => scalar,
3053 _ => {
3054 let ty_resolution = resolve!(ctx, expr);
3055 return Err(Box::new(Error::BadTypeCast {
3056 from_type: ctx.type_resolution_to_string(ty_resolution),
3057 span: function_span,
3058 to_type: ctx.type_to_string(ty),
3059 }));
3060 }
3061 };
3062
3063 (
3064 ir::Expression::As {
3065 expr,
3066 kind: element_scalar.kind,
3067 convert: None,
3068 },
3069 MustUse::Yes,
3070 )
3071 }
3072 "coopLoad" | "coopLoadT" => {
3073 let row_major = function_name.ends_with("T");
3074 let (matrix_ty, matrix_span) = template_params.ty_with_span(self, ctx)?;
3075
3076 let mut args = ctx.prepare_args(arguments, 1, call_span);
3077 let pointer = self.expression(args.next()?, ctx)?;
3078 let (columns, rows, role) = match ctx.module.types[matrix_ty].inner {
3079 ir::TypeInner::CooperativeMatrix {
3080 columns,
3081 rows,
3082 role,
3083 ..
3084 } => (columns, rows, role),
3085 _ => return Err(Box::new(Error::InvalidCooperativeLoadType(matrix_span))),
3086 };
3087 let stride = if args.total_args > 1 {
3088 self.expression(args.next()?, ctx)?
3089 } else {
3090 let stride = if row_major {
3092 columns as u32
3093 } else {
3094 rows as u32
3095 };
3096 ctx.append_expression(
3097 ir::Expression::Literal(ir::Literal::U32(stride)),
3098 Span::UNDEFINED,
3099 )?
3100 };
3101 args.finish()?;
3102
3103 (
3104 crate::Expression::CooperativeLoad {
3105 columns,
3106 rows,
3107 role,
3108 data: crate::CooperativeData {
3109 pointer,
3110 stride,
3111 row_major,
3112 },
3113 },
3114 MustUse::Yes,
3115 )
3116 }
3117 "select" => {
3118 let mut args = ctx.prepare_args(arguments, 3, function_span);
3119
3120 let reject_orig = args.next()?;
3121 let accept_orig = args.next()?;
3122 let mut values = [
3123 self.expression_for_abstract(reject_orig, ctx)?,
3124 self.expression_for_abstract(accept_orig, ctx)?,
3125 ];
3126 let condition = self.expression(args.next()?, ctx)?;
3127
3128 args.finish()?;
3129
3130 let diagnostic_details =
3131 |ctx: &ExpressionContext<'_, '_, '_>,
3132 ty_res: &proc::TypeResolution,
3133 orig_expr| {
3134 (
3135 ctx.ast_expressions.get_span(orig_expr),
3136 format!("`{}`", ctx.as_diagnostic_display(ty_res)),
3137 )
3138 };
3139 for (&value, orig_value) in values.iter().zip([reject_orig, accept_orig]) {
3140 let value_ty_res = resolve!(ctx, value);
3141 if value_ty_res
3142 .inner_with(&ctx.module.types)
3143 .vector_size_and_scalar()
3144 .is_none()
3145 {
3146 let (arg_span, arg_type) =
3147 diagnostic_details(ctx, value_ty_res, orig_value);
3148 return Err(Box::new(Error::SelectUnexpectedArgumentType {
3149 arg_span,
3150 arg_type,
3151 }));
3152 }
3153 }
3154 let mut consensus_scalar = ctx
3155 .automatic_conversion_consensus(None, &values)
3156 .map_err(|_idx| {
3157 let [reject, accept] = values;
3158 let [(reject_span, reject_type), (accept_span, accept_type)] =
3159 [(reject_orig, reject), (accept_orig, accept)].map(
3160 |(orig_expr, expr)| {
3161 let ty_res = &ctx.typifier()[expr];
3162 diagnostic_details(ctx, ty_res, orig_expr)
3163 },
3164 );
3165 Error::SelectRejectAndAcceptHaveNoCommonType {
3166 reject_span,
3167 reject_type,
3168 accept_span,
3169 accept_type,
3170 }
3171 })?;
3172 if !ctx.is_const(condition) {
3173 consensus_scalar = consensus_scalar.concretize();
3174 }
3175
3176 ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;
3177
3178 let [reject, accept] = values;
3179
3180 (
3181 ir::Expression::Select {
3182 reject,
3183 accept,
3184 condition,
3185 },
3186 MustUse::Yes,
3187 )
3188 }
3189 "arrayLength" => {
3190 let mut args = ctx.prepare_args(arguments, 1, function_span);
3191 let expr = self.expression(args.next()?, ctx)?;
3192 args.finish()?;
3193
3194 (ir::Expression::ArrayLength(expr), MustUse::Yes)
3195 }
3196 "atomicLoad" => {
3197 let mut args = ctx.prepare_args(arguments, 1, function_span);
3198 let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?;
3199 args.finish()?;
3200
3201 (ir::Expression::Load { pointer }, MustUse::No)
3202 }
3203 "atomicStore" => {
3204 let mut args = ctx.prepare_args(arguments, 2, function_span);
3205 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3206 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3207 args.finish()?;
3208
3209 let rctx = ctx.runtime_expression_ctx(function_span)?;
3210 rctx.block
3211 .extend(rctx.emitter.finish(&rctx.function.expressions));
3212 rctx.emitter.start(&rctx.function.expressions);
3213 rctx.block
3214 .push(ir::Statement::Store { pointer, value }, function_span);
3215 return Ok(None);
3216 }
3217 "atomicCompareExchangeWeak" => {
3218 let mut args = ctx.prepare_args(arguments, 3, function_span);
3219
3220 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3221
3222 let compare = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3223
3224 let value = args.next()?;
3225 let value_span = ctx.ast_expressions.get_span(value);
3226 let value = self.expression_with_leaf_scalar(value, scalar, ctx)?;
3227
3228 args.finish()?;
3229
3230 let expression = match *resolve_inner!(ctx, value) {
3231 ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult {
3232 ty: ctx.module.generate_predeclared_type(
3233 ir::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
3234 ),
3235 comparison: true,
3236 },
3237 _ => return Err(Box::new(Error::InvalidAtomicOperandType(value_span))),
3238 };
3239
3240 let result = ctx.interrupt_emitter(expression, function_span)?;
3241 let rctx = ctx.runtime_expression_ctx(function_span)?;
3242 rctx.block.push(
3243 ir::Statement::Atomic {
3244 pointer,
3245 fun: ir::AtomicFunction::Exchange {
3246 compare: Some(compare),
3247 },
3248 value,
3249 result: Some(result),
3250 },
3251 function_span,
3252 );
3253 return Ok(Some((result, MustUse::No)));
3254 }
3255 "textureAtomicMin" | "textureAtomicMax" | "textureAtomicAdd"
3256 | "textureAtomicAnd" | "textureAtomicOr" | "textureAtomicXor" => {
3257 let mut args = ctx.prepare_args(arguments, 3, function_span);
3258
3259 let image = args.next()?;
3260 let image_span = ctx.ast_expressions.get_span(image);
3261 let image = self.expression(image, ctx)?;
3262
3263 let coordinate = self.expression(args.next()?, ctx)?;
3264
3265 let (_, arrayed) = ctx.image_data(image, image_span)?;
3266 let array_index = arrayed
3267 .then(|| {
3268 args.min_args += 1;
3269 self.expression(args.next()?, ctx)
3270 })
3271 .transpose()?;
3272
3273 let value = self.expression(args.next()?, ctx)?;
3274
3275 args.finish()?;
3276
3277 let rctx = ctx.runtime_expression_ctx(function_span)?;
3278 rctx.block
3279 .extend(rctx.emitter.finish(&rctx.function.expressions));
3280 rctx.emitter.start(&rctx.function.expressions);
3281 let stmt = ir::Statement::ImageAtomic {
3282 image,
3283 coordinate,
3284 array_index,
3285 fun: match function_name {
3286 "textureAtomicMin" => ir::AtomicFunction::Min,
3287 "textureAtomicMax" => ir::AtomicFunction::Max,
3288 "textureAtomicAdd" => ir::AtomicFunction::Add,
3289 "textureAtomicAnd" => ir::AtomicFunction::And,
3290 "textureAtomicOr" => ir::AtomicFunction::InclusiveOr,
3291 "textureAtomicXor" => ir::AtomicFunction::ExclusiveOr,
3292 _ => unreachable!(),
3293 },
3294 value,
3295 };
3296 rctx.block.push(stmt, function_span);
3297 return Ok(None);
3298 }
3299 "storageBarrier" => {
3300 ctx.prepare_args(arguments, 0, function_span).finish()?;
3301
3302 let rctx = ctx.runtime_expression_ctx(function_span)?;
3303 rctx.block.push(
3304 ir::Statement::ControlBarrier(ir::Barrier::STORAGE),
3305 function_span,
3306 );
3307 return Ok(None);
3308 }
3309 "workgroupBarrier" => {
3310 ctx.prepare_args(arguments, 0, function_span).finish()?;
3311
3312 let rctx = ctx.runtime_expression_ctx(function_span)?;
3313 rctx.block.push(
3314 ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP),
3315 function_span,
3316 );
3317 return Ok(None);
3318 }
3319 "subgroupBarrier" => {
3320 ctx.prepare_args(arguments, 0, function_span).finish()?;
3321
3322 let rctx = ctx.runtime_expression_ctx(function_span)?;
3323 rctx.block.push(
3324 ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP),
3325 function_span,
3326 );
3327 return Ok(None);
3328 }
3329 "textureBarrier" => {
3330 ctx.prepare_args(arguments, 0, function_span).finish()?;
3331
3332 let rctx = ctx.runtime_expression_ctx(function_span)?;
3333 rctx.block.push(
3334 ir::Statement::ControlBarrier(ir::Barrier::TEXTURE),
3335 function_span,
3336 );
3337 return Ok(None);
3338 }
3339 "workgroupUniformLoad" => {
3340 let mut args = ctx.prepare_args(arguments, 1, function_span);
3341 let expr = args.next()?;
3342 args.finish()?;
3343
3344 let pointer = self.expression(expr, ctx)?;
3345 let result_ty = match *resolve_inner!(ctx, pointer) {
3346 ir::TypeInner::Pointer {
3347 base,
3348 space: ir::AddressSpace::WorkGroup,
3349 } => match ctx.module.types[base].inner {
3350 ir::TypeInner::Atomic(scalar) => ctx.module.types.insert(
3353 ir::Type {
3354 name: None,
3355 inner: ir::TypeInner::Scalar(scalar),
3356 },
3357 function_span,
3358 ),
3359 _ => base,
3360 },
3361 ir::TypeInner::ValuePointer {
3362 size,
3363 scalar,
3364 space: ir::AddressSpace::WorkGroup,
3365 } => ctx.module.types.insert(
3366 ir::Type {
3367 name: None,
3368 inner: match size {
3369 Some(size) => ir::TypeInner::Vector { size, scalar },
3370 None => ir::TypeInner::Scalar(scalar),
3371 },
3372 },
3373 function_span,
3374 ),
3375 _ => {
3376 let span = ctx.ast_expressions.get_span(expr);
3377 return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span)));
3378 }
3379 };
3380 let result = ctx.interrupt_emitter(
3381 ir::Expression::WorkGroupUniformLoadResult { ty: result_ty },
3382 function_span,
3383 )?;
3384 let rctx = ctx.runtime_expression_ctx(function_span)?;
3385 rctx.block.push(
3386 ir::Statement::WorkGroupUniformLoad { pointer, result },
3387 function_span,
3388 );
3389
3390 return Ok(Some((result, MustUse::Yes)));
3391 }
3392 "textureStore" => {
3393 let mut args = ctx.prepare_args(arguments, 3, function_span);
3394
3395 let image = args.next()?;
3396 let image_span = ctx.ast_expressions.get_span(image);
3397 let image = self.expression(image, ctx)?;
3398
3399 let coordinate = self.expression(args.next()?, ctx)?;
3400
3401 let (class, arrayed) = ctx.image_data(image, image_span)?;
3402 let array_index = arrayed
3403 .then(|| {
3404 args.min_args += 1;
3405 self.expression(args.next()?, ctx)
3406 })
3407 .transpose()?;
3408 let scalar = if let ir::ImageClass::Storage { format, .. } = class {
3409 format.into()
3410 } else {
3411 return Err(Box::new(Error::NotStorageTexture(image_span)));
3412 };
3413
3414 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3415
3416 args.finish()?;
3417
3418 let rctx = ctx.runtime_expression_ctx(function_span)?;
3419 rctx.block
3420 .extend(rctx.emitter.finish(&rctx.function.expressions));
3421 rctx.emitter.start(&rctx.function.expressions);
3422 let stmt = ir::Statement::ImageStore {
3423 image,
3424 coordinate,
3425 array_index,
3426 value,
3427 };
3428 rctx.block.push(stmt, function_span);
3429 return Ok(None);
3430 }
3431 "textureLoad" => {
3432 let mut args = ctx.prepare_args(arguments, 2, function_span);
3433
3434 let image = args.next()?;
3435 let image_span = ctx.ast_expressions.get_span(image);
3436 let image = self.expression(image, ctx)?;
3437
3438 let coordinate = self.expression(args.next()?, ctx)?;
3439
3440 let (class, arrayed) = ctx.image_data(image, image_span)?;
3441 let array_index = arrayed
3442 .then(|| {
3443 args.min_args += 1;
3444 self.expression(args.next()?, ctx)
3445 })
3446 .transpose()?;
3447
3448 let level = class
3449 .is_mipmapped()
3450 .then(|| {
3451 args.min_args += 1;
3452 self.expression(args.next()?, ctx)
3453 })
3454 .transpose()?;
3455
3456 let sample = class
3457 .is_multisampled()
3458 .then(|| self.expression(args.next()?, ctx))
3459 .transpose()?;
3460
3461 args.finish()?;
3462
3463 (
3464 ir::Expression::ImageLoad {
3465 image,
3466 coordinate,
3467 array_index,
3468 level,
3469 sample,
3470 },
3471 MustUse::Yes,
3472 )
3473 }
3474 "textureDimensions" => {
3475 let mut args = ctx.prepare_args(arguments, 1, function_span);
3476 let image = self.expression(args.next()?, ctx)?;
3477 let level = args
3478 .next()
3479 .map(|arg| self.expression(arg, ctx))
3480 .ok()
3481 .transpose()?;
3482 args.finish()?;
3483
3484 (
3485 ir::Expression::ImageQuery {
3486 image,
3487 query: ir::ImageQuery::Size { level },
3488 },
3489 MustUse::Yes,
3490 )
3491 }
3492 "textureNumLevels" => {
3493 let mut args = ctx.prepare_args(arguments, 1, function_span);
3494 let image = self.expression(args.next()?, ctx)?;
3495 args.finish()?;
3496
3497 (
3498 ir::Expression::ImageQuery {
3499 image,
3500 query: ir::ImageQuery::NumLevels,
3501 },
3502 MustUse::Yes,
3503 )
3504 }
3505 "textureNumLayers" => {
3506 let mut args = ctx.prepare_args(arguments, 1, function_span);
3507 let image = self.expression(args.next()?, ctx)?;
3508 args.finish()?;
3509
3510 (
3511 ir::Expression::ImageQuery {
3512 image,
3513 query: ir::ImageQuery::NumLayers,
3514 },
3515 MustUse::Yes,
3516 )
3517 }
3518 "textureNumSamples" => {
3519 let mut args = ctx.prepare_args(arguments, 1, function_span);
3520 let image = self.expression(args.next()?, ctx)?;
3521 args.finish()?;
3522
3523 (
3524 ir::Expression::ImageQuery {
3525 image,
3526 query: ir::ImageQuery::NumSamples,
3527 },
3528 MustUse::Yes,
3529 )
3530 }
3531 "rayQueryInitialize" => {
3532 let mut args = ctx.prepare_args(arguments, 3, function_span);
3533 let query = self.ray_query_pointer(args.next()?, ctx)?;
3534 let acceleration_structure = self.expression(args.next()?, ctx)?;
3535 let descriptor = self.expression(args.next()?, ctx)?;
3536 args.finish()?;
3537
3538 let _ = ctx.module.generate_ray_desc_type();
3539 let fun = ir::RayQueryFunction::Initialize {
3540 acceleration_structure,
3541 descriptor,
3542 };
3543
3544 let rctx = ctx.runtime_expression_ctx(function_span)?;
3545 rctx.block
3546 .extend(rctx.emitter.finish(&rctx.function.expressions));
3547 rctx.emitter.start(&rctx.function.expressions);
3548 rctx.block
3549 .push(ir::Statement::RayQuery { query, fun }, function_span);
3550 return Ok(None);
3551 }
3552 "getCommittedHitVertexPositions" => {
3553 let mut args = ctx.prepare_args(arguments, 1, function_span);
3554 let query = self.ray_query_pointer(args.next()?, ctx)?;
3555 args.finish()?;
3556
3557 let _ = ctx.module.generate_vertex_return_type();
3558
3559 (
3560 ir::Expression::RayQueryVertexPositions {
3561 query,
3562 committed: true,
3563 },
3564 MustUse::No,
3565 )
3566 }
3567 "getCandidateHitVertexPositions" => {
3568 let mut args = ctx.prepare_args(arguments, 1, function_span);
3569 let query = self.ray_query_pointer(args.next()?, ctx)?;
3570 args.finish()?;
3571
3572 let _ = ctx.module.generate_vertex_return_type();
3573
3574 (
3575 ir::Expression::RayQueryVertexPositions {
3576 query,
3577 committed: false,
3578 },
3579 MustUse::No,
3580 )
3581 }
3582 "rayQueryProceed" => {
3583 let mut args = ctx.prepare_args(arguments, 1, function_span);
3584 let query = self.ray_query_pointer(args.next()?, ctx)?;
3585 args.finish()?;
3586
3587 let result = ctx
3588 .interrupt_emitter(ir::Expression::RayQueryProceedResult, function_span)?;
3589 let fun = ir::RayQueryFunction::Proceed { result };
3590 let rctx = ctx.runtime_expression_ctx(function_span)?;
3591 rctx.block
3592 .push(ir::Statement::RayQuery { query, fun }, function_span);
3593 return Ok(Some((result, MustUse::No)));
3594 }
3595 "rayQueryGenerateIntersection" => {
3596 let mut args = ctx.prepare_args(arguments, 2, function_span);
3597 let query = self.ray_query_pointer(args.next()?, ctx)?;
3598 let hit_t = self.expression(args.next()?, ctx)?;
3599 args.finish()?;
3600
3601 let fun = ir::RayQueryFunction::GenerateIntersection { hit_t };
3602 let rctx = ctx.runtime_expression_ctx(function_span)?;
3603 rctx.block
3604 .push(ir::Statement::RayQuery { query, fun }, function_span);
3605 return Ok(None);
3606 }
3607 "rayQueryConfirmIntersection" => {
3608 let mut args = ctx.prepare_args(arguments, 1, function_span);
3609 let query = self.ray_query_pointer(args.next()?, ctx)?;
3610 args.finish()?;
3611
3612 let fun = ir::RayQueryFunction::ConfirmIntersection;
3613 let rctx = ctx.runtime_expression_ctx(function_span)?;
3614 rctx.block
3615 .push(ir::Statement::RayQuery { query, fun }, function_span);
3616 return Ok(None);
3617 }
3618 "rayQueryTerminate" => {
3619 let mut args = ctx.prepare_args(arguments, 1, function_span);
3620 let query = self.ray_query_pointer(args.next()?, ctx)?;
3621 args.finish()?;
3622
3623 let fun = ir::RayQueryFunction::Terminate;
3624 let rctx = ctx.runtime_expression_ctx(function_span)?;
3625 rctx.block
3626 .push(ir::Statement::RayQuery { query, fun }, function_span);
3627 return Ok(None);
3628 }
3629 "rayQueryGetCommittedIntersection" => {
3630 let mut args = ctx.prepare_args(arguments, 1, function_span);
3631 let query = self.ray_query_pointer(args.next()?, ctx)?;
3632 args.finish()?;
3633
3634 let _ = ctx.module.generate_ray_intersection_type();
3635 (
3636 ir::Expression::RayQueryGetIntersection {
3637 query,
3638 committed: true,
3639 },
3640 MustUse::No,
3641 )
3642 }
3643 "rayQueryGetCandidateIntersection" => {
3644 let mut args = ctx.prepare_args(arguments, 1, function_span);
3645 let query = self.ray_query_pointer(args.next()?, ctx)?;
3646 args.finish()?;
3647
3648 let _ = ctx.module.generate_ray_intersection_type();
3649 (
3650 ir::Expression::RayQueryGetIntersection {
3651 query,
3652 committed: false,
3653 },
3654 MustUse::No,
3655 )
3656 }
3657 "subgroupBallot" => {
3658 let mut args = ctx.prepare_args(arguments, 0, function_span);
3659 let predicate = if arguments.len() == 1 {
3660 Some(self.expression(args.next()?, ctx)?)
3661 } else {
3662 None
3663 };
3664 args.finish()?;
3665
3666 let result =
3667 ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, function_span)?;
3668 let rctx = ctx.runtime_expression_ctx(function_span)?;
3669 rctx.block.push(
3670 ir::Statement::SubgroupBallot { result, predicate },
3671 function_span,
3672 );
3673 return Ok(Some((result, MustUse::Yes)));
3674 }
3675 "quadSwapX" => {
3676 let mut args = ctx.prepare_args(arguments, 1, function_span);
3677
3678 let argument = self.expression(args.next()?, ctx)?;
3679 args.finish()?;
3680
3681 let ty = ctx.register_type(argument)?;
3682
3683 let result = ctx.interrupt_emitter(
3684 crate::Expression::SubgroupOperationResult { ty },
3685 function_span,
3686 )?;
3687 let rctx = ctx.runtime_expression_ctx(function_span)?;
3688 rctx.block.push(
3689 crate::Statement::SubgroupGather {
3690 mode: crate::GatherMode::QuadSwap(crate::Direction::X),
3691 argument,
3692 result,
3693 },
3694 function_span,
3695 );
3696 return Ok(Some((result, MustUse::Yes)));
3697 }
3698 "quadSwapY" => {
3699 let mut args = ctx.prepare_args(arguments, 1, function_span);
3700
3701 let argument = self.expression(args.next()?, ctx)?;
3702 args.finish()?;
3703
3704 let ty = ctx.register_type(argument)?;
3705
3706 let result = ctx.interrupt_emitter(
3707 crate::Expression::SubgroupOperationResult { ty },
3708 function_span,
3709 )?;
3710 let rctx = ctx.runtime_expression_ctx(function_span)?;
3711 rctx.block.push(
3712 crate::Statement::SubgroupGather {
3713 mode: crate::GatherMode::QuadSwap(crate::Direction::Y),
3714 argument,
3715 result,
3716 },
3717 function_span,
3718 );
3719 return Ok(Some((result, MustUse::Yes)));
3720 }
3721 "quadSwapDiagonal" => {
3722 let mut args = ctx.prepare_args(arguments, 1, function_span);
3723
3724 let argument = self.expression(args.next()?, ctx)?;
3725 args.finish()?;
3726
3727 let ty = ctx.register_type(argument)?;
3728
3729 let result = ctx.interrupt_emitter(
3730 crate::Expression::SubgroupOperationResult { ty },
3731 function_span,
3732 )?;
3733 let rctx = ctx.runtime_expression_ctx(function_span)?;
3734 rctx.block.push(
3735 crate::Statement::SubgroupGather {
3736 mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal),
3737 argument,
3738 result,
3739 },
3740 function_span,
3741 );
3742 return Ok(Some((result, MustUse::Yes)));
3743 }
3744 "coopStore" | "coopStoreT" => {
3745 let row_major = function_name.ends_with("T");
3746
3747 let mut args = ctx.prepare_args(arguments, 2, function_span);
3748 let target = self.expression(args.next()?, ctx)?;
3749 let pointer = self.expression(args.next()?, ctx)?;
3750 let stride = if args.total_args > 2 {
3751 self.expression(args.next()?, ctx)?
3752 } else {
3753 let stride = match *resolve_inner!(ctx, target) {
3755 ir::TypeInner::CooperativeMatrix { columns, rows, .. } => {
3756 if row_major {
3757 columns as u32
3758 } else {
3759 rows as u32
3760 }
3761 }
3762 _ => 0,
3763 };
3764 ctx.append_expression(
3765 ir::Expression::Literal(ir::Literal::U32(stride)),
3766 Span::UNDEFINED,
3767 )?
3768 };
3769 args.finish()?;
3770
3771 let rctx = ctx.runtime_expression_ctx(function_span)?;
3772 rctx.block.push(
3773 crate::Statement::CooperativeStore {
3774 target,
3775 data: crate::CooperativeData {
3776 pointer,
3777 stride,
3778 row_major,
3779 },
3780 },
3781 function_span,
3782 );
3783 return Ok(None);
3784 }
3785 "coopMultiplyAdd" => {
3786 let mut args = ctx.prepare_args(arguments, 3, function_span);
3787 let a = self.expression(args.next()?, ctx)?;
3788 let b = self.expression(args.next()?, ctx)?;
3789 let c = self.expression(args.next()?, ctx)?;
3790 args.finish()?;
3791
3792 (
3793 ir::Expression::CooperativeMultiplyAdd { a, b, c },
3794 MustUse::Yes,
3795 )
3796 }
3797 "traceRay" => {
3798 let mut args = ctx.prepare_args(arguments, 3, function_span);
3799 let acceleration_structure = self.expression(args.next()?, ctx)?;
3800 let descriptor = self.expression(args.next()?, ctx)?;
3801 let payload = self.expression(args.next()?, ctx)?;
3802 args.finish()?;
3803
3804 let _ = ctx.module.generate_ray_desc_type();
3805 let fun = ir::RayPipelineFunction::TraceRay {
3806 acceleration_structure,
3807 descriptor,
3808 payload,
3809 };
3810
3811 let rctx = ctx.runtime_expression_ctx(function_span)?;
3812 rctx.block
3813 .extend(rctx.emitter.finish(&rctx.function.expressions));
3814 rctx.emitter.start(&rctx.function.expressions);
3815 rctx.block
3816 .push(ir::Statement::RayPipelineFunction(fun), function_span);
3817 return Ok(None);
3818 }
3819 _ => return Err(Box::new(Error::UnknownIdent(function_span, function_name))),
3820 }
3821 };
3822
3823 let expr = ctx.append_expression(expr, function_span)?;
3824 Ok(Some((expr, must_use)))
3825 }
3826
3827 fn call(
3846 &mut self,
3847 call_phrase: &ast::CallPhrase<'source>,
3848 span: Span,
3849 ctx: &mut ExpressionContext<'source, '_, '_>,
3850 is_statement: bool,
3851 ) -> Result<'source, Option<Handle<ir::Expression>>> {
3852 let function_name = match call_phrase.function.ident {
3853 ast::IdentExpr::Unresolved(name) => name,
3854 ast::IdentExpr::Local(_) => {
3855 return Err(Box::new(Error::CalledLocalDecl(
3856 call_phrase.function.ident_span,
3857 )))
3858 }
3859 };
3860 let mut function_span = call_phrase.function.ident_span;
3861 function_span.subsume(call_phrase.function.template_list_span);
3862 let arguments = call_phrase.arguments.as_slice();
3863
3864 let mut tl = TemplateListIter::new(function_span, &call_phrase.function.template_list);
3865
3866 let result = match ctx.globals.get(function_name) {
3867 Some(&LoweredGlobalDecl::Type(ty)) => {
3868 tl.finish(ctx)?;
3870
3871 let handle =
3872 self.construct(span, Constructor::Type(ty), function_span, arguments, ctx)?;
3873 Some((handle, MustUse::Yes))
3874 }
3875 Some(
3876 &LoweredGlobalDecl::Const(_)
3877 | &LoweredGlobalDecl::Override(_)
3878 | &LoweredGlobalDecl::Var(_),
3879 ) => {
3880 return Err(Box::new(Error::Unexpected(
3881 function_span,
3882 ExpectedToken::Function,
3883 )))
3884 }
3885 Some(&LoweredGlobalDecl::EntryPoint(_)) => {
3886 return Err(Box::new(Error::CalledEntryPoint(function_span)));
3887 }
3888 Some(&LoweredGlobalDecl::Function {
3889 handle: function,
3890 must_use,
3891 }) => {
3892 tl.finish(ctx)?;
3894
3895 let arguments = arguments
3896 .iter()
3897 .enumerate()
3898 .map(|(i, &arg)| {
3899 let Some(&ir::FunctionArgument {
3901 ty: parameter_ty, ..
3902 }) = ctx.module.functions[function].arguments.get(i)
3903 else {
3904 return self.expression(arg, ctx);
3907 };
3908
3909 let expr = self.expression_for_abstract(arg, ctx)?;
3910 ctx.try_automatic_conversions(
3911 expr,
3912 &proc::TypeResolution::Handle(parameter_ty),
3913 ctx.ast_expressions.get_span(arg),
3914 )
3915 })
3916 .collect::<Result<Vec<_>>>()?;
3917
3918 let has_result = ctx.module.functions[function].result.is_some();
3919
3920 let rctx = ctx.runtime_expression_ctx(span)?;
3921 rctx.block
3923 .extend(rctx.emitter.finish(&rctx.function.expressions));
3924 let result = has_result.then(|| {
3925 let result = rctx
3926 .function
3927 .expressions
3928 .append(ir::Expression::CallResult(function), span);
3929 rctx.local_expression_kind_tracker
3930 .insert(result, proc::ExpressionKind::Runtime);
3931 (result, must_use.into())
3932 });
3933 rctx.emitter.start(&rctx.function.expressions);
3934 rctx.block.push(
3935 ir::Statement::Call {
3936 function,
3937 arguments,
3938 result: result.map(|(expr, _)| expr),
3939 },
3940 span,
3941 );
3942
3943 result
3944 }
3945 None => {
3946 let ty = conv::map_predeclared_type(
3948 &ctx.enable_extensions,
3949 function_span,
3950 function_name,
3951 )?;
3952 if let Some(ty) = ty {
3953 let empty_template_list = call_phrase.function.template_list.is_empty();
3954 let constructor_ty = match ty {
3955 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Vector {
3956 size,
3957 }) if empty_template_list => Constructor::PartialVector { size },
3958 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Matrix {
3959 columns,
3960 rows,
3961 }) if empty_template_list => Constructor::PartialMatrix { columns, rows },
3962 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Array)
3963 if empty_template_list =>
3964 {
3965 Constructor::PartialArray
3966 }
3967 conv::PredeclaredType::TypeGenerator(
3968 conv::TypeGenerator::CooperativeMatrix { .. },
3969 ) if empty_template_list => {
3970 return Err(Box::new(Error::UnderspecifiedCooperativeMatrix));
3971 }
3972 _ => Constructor::Type(self.finalize_type(ctx, ty, &mut tl, None)?),
3973 };
3974 tl.finish(ctx)?;
3975 let handle =
3976 self.construct(span, constructor_ty, function_span, arguments, ctx)?;
3977 Some((handle, MustUse::Yes))
3978 } else {
3979 let result = self.call_builtin(
3981 function_name,
3982 function_span,
3983 arguments,
3984 &mut tl,
3985 span,
3986 ctx,
3987 is_statement,
3988 )?;
3989 tl.finish(ctx)?;
3990 result
3991 }
3992 }
3993 };
3994
3995 let result_used = !is_statement;
3996 if matches!(result, Some((_, MustUse::Yes))) && !result_used {
3997 return Err(Box::new(Error::FunctionMustUseUnused(function_span)));
3998 }
3999 Ok(result.map(|(expr, _)| expr))
4000 }
4001
4002 fn math_function_helper(
4013 &mut self,
4014 span: Span,
4015 fun: ir::MathFunction,
4016 ast_arguments: &[Handle<ast::Expression<'source>>],
4017 ctx: &mut ExpressionContext<'source, '_, '_>,
4018 ) -> Result<'source, ir::Expression> {
4019 let mut lowered_arguments = Vec::with_capacity(ast_arguments.len());
4020 for &arg in ast_arguments {
4021 let lowered = self.expression_for_abstract(arg, ctx)?;
4022 ctx.grow_types(lowered)?;
4023 lowered_arguments.push(lowered);
4024 }
4025
4026 let fun_overloads = fun.overloads();
4027 let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?;
4028 self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?;
4029
4030 if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion {
4034 ctx.module.generate_predeclared_type(predeclared);
4035 }
4036
4037 Ok(ir::Expression::Math {
4038 fun,
4039 arg: lowered_arguments[0],
4040 arg1: lowered_arguments.get(1).cloned(),
4041 arg2: lowered_arguments.get(2).cloned(),
4042 arg3: lowered_arguments.get(3).cloned(),
4043 })
4044 }
4045
4046 fn resolve_overloads<O, F>(
4057 &self,
4058 span: Span,
4059 fun: F,
4060 overloads: O,
4061 arguments: &[Handle<ir::Expression>],
4062 ctx: &ExpressionContext<'source, '_, '_>,
4063 ) -> Result<'source, proc::Rule>
4064 where
4065 O: proc::OverloadSet,
4066 F: TryToWgsl + core::fmt::Debug + Copy,
4067 {
4068 let mut remaining_overloads = overloads.clone();
4069 let min_arguments = remaining_overloads.min_arguments();
4070 let max_arguments = remaining_overloads.max_arguments();
4071 if arguments.len() < min_arguments {
4072 return Err(Box::new(Error::WrongArgumentCount {
4073 span,
4074 expected: min_arguments as u32..max_arguments as u32,
4075 found: arguments.len() as u32,
4076 }));
4077 }
4078 if arguments.len() > max_arguments {
4079 return Err(Box::new(Error::TooManyArguments {
4080 function: fun.to_wgsl_for_diagnostics(),
4081 call_span: span,
4082 arg_span: ctx.get_expression_span(arguments[max_arguments]),
4083 max_arguments: max_arguments as _,
4084 }));
4085 }
4086
4087 log::debug!(
4088 "Initial overloads: {:#?}",
4089 remaining_overloads.for_debug(&ctx.module.types)
4090 );
4091
4092 for (arg_index, &arg) in arguments.iter().enumerate() {
4093 let arg_type_resolution = &ctx.typifier()[arg];
4094 let arg_inner = arg_type_resolution.inner_with(&ctx.module.types);
4095 log::debug!(
4096 "Supplying argument {arg_index} of type {:?}",
4097 arg_type_resolution.for_debug(&ctx.module.types)
4098 );
4099 let next_remaining_overloads =
4100 remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types);
4101
4102 log::debug!(
4110 "Remaining overloads: {:#?}",
4111 next_remaining_overloads.for_debug(&ctx.module.types)
4112 );
4113
4114 if next_remaining_overloads.is_empty() {
4117 let function = fun.to_wgsl_for_diagnostics();
4118 let call_span = span;
4119 let arg_span = ctx.get_expression_span(arg);
4120 let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string();
4121
4122 let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types);
4125 if only_this_argument.is_empty() {
4126 let allowed: Vec<String> = overloads
4130 .allowed_args(arg_index, &ctx.module.to_ctx())
4131 .iter()
4132 .map(|ty| ctx.type_resolution_to_string(ty))
4133 .collect();
4134
4135 if allowed.is_empty() {
4136 unreachable!("expected all overloads to have the same arity");
4142 }
4143
4144 return Err(Box::new(Error::WrongArgumentType {
4147 function,
4148 call_span,
4149 arg_span,
4150 arg_index: arg_index as u32,
4151 arg_ty,
4152 allowed,
4153 }));
4154 }
4155
4156 let allowed: Vec<String> = remaining_overloads
4164 .allowed_args(arg_index, &ctx.module.to_ctx())
4165 .iter()
4166 .map(|ty| ctx.type_resolution_to_string(ty))
4167 .collect();
4168
4169 let mut remaining_overloads = overloads;
4172 for (prior_index, &prior_expr) in arguments.iter().enumerate() {
4173 let prior_type_resolution = &ctx.typifier()[prior_expr];
4174 let prior_ty = prior_type_resolution.inner_with(&ctx.module.types);
4175 remaining_overloads =
4176 remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types);
4177 if remaining_overloads
4178 .arg(arg_index, arg_inner, &ctx.module.types)
4179 .is_empty()
4180 {
4181 let inconsistent_span = ctx.get_expression_span(arguments[prior_index]);
4183 let inconsistent_ty =
4184 ctx.as_diagnostic_display(prior_type_resolution).to_string();
4185
4186 if allowed.is_empty() {
4187 unreachable!("expected all overloads to have the same arity");
4194 }
4195
4196 return Err(Box::new(Error::InconsistentArgumentType {
4198 function,
4199 call_span,
4200 arg_span,
4201 arg_index: arg_index as u32,
4202 arg_ty,
4203 inconsistent_span,
4204 inconsistent_index: prior_index as u32,
4205 inconsistent_ty,
4206 allowed,
4207 }));
4208 }
4209 }
4210 unreachable!("Failed to eliminate argument type when re-tried");
4211 }
4212 remaining_overloads = next_remaining_overloads;
4213 }
4214
4215 Ok(remaining_overloads.most_preferred())
4218 }
4219
4220 fn apply_automatic_conversions_for_call(
4226 &self,
4227 rule: &proc::Rule,
4228 arguments: &mut [Handle<ir::Expression>],
4229 ctx: &mut ExpressionContext<'source, '_, '_>,
4230 ) -> Result<'source, ()> {
4231 for (i, argument) in arguments.iter_mut().enumerate() {
4232 let goal_inner = rule.arguments[i].inner_with(&ctx.module.types);
4233 let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) {
4234 Some(goal_scalar) => {
4235 let arg_span = ctx.get_expression_span(*argument);
4236 ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)?
4237 }
4238 None => *argument,
4240 };
4241
4242 *argument = converted;
4243 }
4244
4245 Ok(())
4246 }
4247
4248 fn atomic_pointer(
4249 &mut self,
4250 expr: Handle<ast::Expression<'source>>,
4251 ctx: &mut ExpressionContext<'source, '_, '_>,
4252 ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> {
4253 let span = ctx.ast_expressions.get_span(expr);
4254 let pointer = self.expression(expr, ctx)?;
4255
4256 match *resolve_inner!(ctx, pointer) {
4257 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4258 ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)),
4259 ref other => {
4260 log::error!("Pointer type to {other:?} passed to atomic op");
4261 Err(Box::new(Error::InvalidAtomicPointer(span)))
4262 }
4263 },
4264 ref other => {
4265 log::error!("Type {other:?} passed to atomic op");
4266 Err(Box::new(Error::InvalidAtomicPointer(span)))
4267 }
4268 }
4269 }
4270
4271 fn atomic_helper(
4272 &mut self,
4273 span: Span,
4274 fun: ir::AtomicFunction,
4275 args: &[Handle<ast::Expression<'source>>],
4276 is_statement: bool,
4277 ctx: &mut ExpressionContext<'source, '_, '_>,
4278 ) -> Result<'source, Option<Handle<ir::Expression>>> {
4279 let mut args = ctx.prepare_args(args, 2, span);
4280
4281 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
4282 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
4283 let value_inner = resolve_inner!(ctx, value);
4284 args.finish()?;
4285
4286 let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max)
4291 && matches!(
4292 *value_inner,
4293 ir::TypeInner::Scalar(ir::Scalar { width: 8, .. })
4294 );
4295 let result = if is_64_bit_min_max && is_statement {
4296 let rctx = ctx.runtime_expression_ctx(span)?;
4297 rctx.block
4298 .extend(rctx.emitter.finish(&rctx.function.expressions));
4299 rctx.emitter.start(&rctx.function.expressions);
4300 None
4301 } else {
4302 let ty = ctx.register_type(value)?;
4303 Some(ctx.interrupt_emitter(
4304 ir::Expression::AtomicResult {
4305 ty,
4306 comparison: false,
4307 },
4308 span,
4309 )?)
4310 };
4311 let rctx = ctx.runtime_expression_ctx(span)?;
4312 rctx.block.push(
4313 ir::Statement::Atomic {
4314 pointer,
4315 fun,
4316 value,
4317 result,
4318 },
4319 span,
4320 );
4321 Ok(result)
4322 }
4323
4324 fn texture_sample_helper(
4325 &mut self,
4326 fun: Texture,
4327 args: &[Handle<ast::Expression<'source>>],
4328 span: Span,
4329 ctx: &mut ExpressionContext<'source, '_, '_>,
4330 ) -> Result<'source, ir::Expression> {
4331 let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);
4332
4333 fn get_image_and_span<'source>(
4334 lowerer: &mut Lowerer<'source, '_>,
4335 args: &mut ArgumentContext<'_, 'source>,
4336 ctx: &mut ExpressionContext<'source, '_, '_>,
4337 ) -> Result<'source, (Handle<ir::Expression>, Span)> {
4338 let image = args.next()?;
4339 let image_span = ctx.ast_expressions.get_span(image);
4340 let image = lowerer.expression_for_abstract(image, ctx)?;
4341 Ok((image, image_span))
4342 }
4343
4344 let image;
4345 let image_span;
4346 let gather;
4347 match fun {
4348 Texture::Gather => {
4349 let image_or_component = args.next()?;
4350 let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);
4351 let lowered_image_or_component = self.expression(image_or_component, ctx)?;
4353
4354 match *resolve_inner!(ctx, lowered_image_or_component) {
4355 ir::TypeInner::Image {
4356 class: ir::ImageClass::Depth { .. },
4357 ..
4358 } => {
4359 image = lowered_image_or_component;
4360 image_span = image_or_component_span;
4361 gather = Some(ir::SwizzleComponent::X);
4362 }
4363 _ => {
4364 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4365 gather = Some(ctx.gather_component(
4366 lowered_image_or_component,
4367 image_or_component_span,
4368 span,
4369 )?);
4370 }
4371 }
4372 }
4373 Texture::GatherCompare => {
4374 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4375 gather = Some(ir::SwizzleComponent::X);
4376 }
4377
4378 _ => {
4379 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4380 gather = None;
4381 }
4382 };
4383
4384 let sampler = self.expression_for_abstract(args.next()?, ctx)?;
4385
4386 let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4387 let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge);
4388
4389 let (class, arrayed) = ctx.image_data(image, image_span)?;
4390 let array_index = arrayed
4391 .then(|| self.expression(args.next()?, ctx))
4392 .transpose()?;
4393
4394 let level;
4395 let depth_ref;
4396 match fun {
4397 Texture::Gather => {
4398 level = ir::SampleLevel::Zero;
4399 depth_ref = None;
4400 }
4401 Texture::GatherCompare => {
4402 let reference =
4403 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4404 level = ir::SampleLevel::Zero;
4405 depth_ref = Some(reference);
4406 }
4407
4408 Texture::Sample => {
4409 level = ir::SampleLevel::Auto;
4410 depth_ref = None;
4411 }
4412 Texture::SampleBias => {
4413 let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4414 level = ir::SampleLevel::Bias(bias);
4415 depth_ref = None;
4416 }
4417 Texture::SampleCompare => {
4418 let reference =
4419 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4420 level = ir::SampleLevel::Auto;
4421 depth_ref = Some(reference);
4422 }
4423 Texture::SampleCompareLevel => {
4424 let reference =
4425 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4426 level = ir::SampleLevel::Zero;
4427 depth_ref = Some(reference);
4428 }
4429 Texture::SampleGrad => {
4430 let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4431 let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4432 level = ir::SampleLevel::Gradient { x, y };
4433 depth_ref = None;
4434 }
4435 Texture::SampleLevel => {
4436 let exact = match class {
4437 ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?,
4440
4441 ir::ImageClass::Sampled { .. } => {
4444 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?
4445 }
4446
4447 ir::ImageClass::Storage { .. } | ir::ImageClass::External => {
4451 self.expression(args.next()?, ctx)?
4452 }
4453 };
4454 level = ir::SampleLevel::Exact(exact);
4455 depth_ref = None;
4456 }
4457 Texture::SampleBaseClampToEdge => {
4458 level = crate::SampleLevel::Zero;
4459 depth_ref = None;
4460 }
4461 };
4462
4463 let offset = args
4464 .next()
4465 .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const()))
4466 .ok()
4467 .transpose()?;
4468
4469 args.finish()?;
4470
4471 Ok(ir::Expression::ImageSample {
4472 image,
4473 sampler,
4474 gather,
4475 coordinate,
4476 array_index,
4477 offset,
4478 level,
4479 depth_ref,
4480 clamp_to_edge,
4481 })
4482 }
4483
4484 fn subgroup_operation_helper(
4485 &mut self,
4486 span: Span,
4487 op: ir::SubgroupOperation,
4488 collective_op: ir::CollectiveOperation,
4489 arguments: &[Handle<ast::Expression<'source>>],
4490 ctx: &mut ExpressionContext<'source, '_, '_>,
4491 ) -> Result<'source, Handle<ir::Expression>> {
4492 let mut args = ctx.prepare_args(arguments, 1, span);
4493
4494 let argument = self.expression(args.next()?, ctx)?;
4495 args.finish()?;
4496
4497 let ty = ctx.register_type(argument)?;
4498
4499 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4500 let rctx = ctx.runtime_expression_ctx(span)?;
4501 rctx.block.push(
4502 ir::Statement::SubgroupCollectiveOperation {
4503 op,
4504 collective_op,
4505 argument,
4506 result,
4507 },
4508 span,
4509 );
4510 Ok(result)
4511 }
4512
4513 fn subgroup_gather_helper(
4514 &mut self,
4515 span: Span,
4516 mode: SubgroupGather,
4517 arguments: &[Handle<ast::Expression<'source>>],
4518 ctx: &mut ExpressionContext<'source, '_, '_>,
4519 ) -> Result<'source, Handle<ir::Expression>> {
4520 let mut args = ctx.prepare_args(arguments, 2, span);
4521
4522 let argument = self.expression(args.next()?, ctx)?;
4523
4524 use SubgroupGather as Sg;
4525 let mode = if let Sg::BroadcastFirst = mode {
4526 ir::GatherMode::BroadcastFirst
4527 } else {
4528 let index = self.expression(args.next()?, ctx)?;
4529 match mode {
4530 Sg::BroadcastFirst => unreachable!(),
4531 Sg::Broadcast => ir::GatherMode::Broadcast(index),
4532 Sg::Shuffle => ir::GatherMode::Shuffle(index),
4533 Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index),
4534 Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index),
4535 Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index),
4536 Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index),
4537 }
4538 };
4539
4540 args.finish()?;
4541
4542 let ty = ctx.register_type(argument)?;
4543
4544 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4545 let rctx = ctx.runtime_expression_ctx(span)?;
4546 rctx.block.push(
4547 ir::Statement::SubgroupGather {
4548 mode,
4549 argument,
4550 result,
4551 },
4552 span,
4553 );
4554 Ok(result)
4555 }
4556
4557 fn r#struct(
4558 &mut self,
4559 s: &ast::Struct<'source>,
4560 span: Span,
4561 ctx: &mut GlobalContext<'source, '_, '_>,
4562 ) -> Result<'source, Handle<ir::Type>> {
4563 let mut offset = 0;
4564 let mut struct_alignment = proc::Alignment::ONE;
4565 let mut members = Vec::with_capacity(s.members.len());
4566
4567 let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new();
4568
4569 for member in s.members.iter() {
4570 let ty = self.resolve_ast_type(&member.ty, &mut ctx.as_const())?;
4571
4572 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
4573 let LayoutErrorInner::TooLarge = err.inner else {
4574 unreachable!("unexpected layout error: {err:?}");
4575 };
4576 if ty == err.ty {
4582 Box::new(Error::StructMemberTooLarge {
4583 member_name_span: member.name.span,
4584 })
4585 } else {
4586 Box::new(Error::TypeTooLarge {
4589 span: ctx.module.types.get_span(err.ty),
4590 })
4591 }
4592 })?;
4593
4594 let member_min_size = ctx.layouter[ty].size;
4595 let member_min_alignment = ctx.layouter[ty].alignment;
4596
4597 let member_size = if let Some(size_expr) = member.size {
4598 let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;
4599 if size < member_min_size {
4600 return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size)));
4601 } else {
4602 size
4603 }
4604 } else {
4605 member_min_size
4606 };
4607
4608 let member_alignment = if let Some(align_expr) = member.align {
4609 let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;
4610 if let Some(alignment) = proc::Alignment::new(align) {
4611 if alignment < member_min_alignment {
4612 return Err(Box::new(Error::AlignAttributeTooLow(
4613 span,
4614 member_min_alignment,
4615 )));
4616 } else {
4617 alignment
4618 }
4619 } else {
4620 return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span)));
4621 }
4622 } else {
4623 member_min_alignment
4624 };
4625
4626 let binding = self.binding(&member.binding, ty, ctx)?;
4627
4628 offset = member_alignment.round_up(offset);
4629 struct_alignment = struct_alignment.max(member_alignment);
4630
4631 if !member.doc_comments.is_empty() {
4632 doc_comments.push(Some(
4633 member.doc_comments.iter().map(|s| s.to_string()).collect(),
4634 ));
4635 }
4636 members.push(ir::StructMember {
4637 name: Some(member.name.name.to_owned()),
4638 ty,
4639 binding,
4640 offset,
4641 });
4642
4643 offset += member_size;
4644 if offset > crate::valid::MAX_TYPE_SIZE {
4645 return Err(Box::new(Error::TypeTooLarge { span }));
4646 }
4647 }
4648
4649 let size = struct_alignment.round_up(offset);
4650 let inner = ir::TypeInner::Struct {
4651 members,
4652 span: size,
4653 };
4654
4655 let handle = ctx.module.types.insert(
4656 ir::Type {
4657 name: Some(s.name.name.to_string()),
4658 inner,
4659 },
4660 span,
4661 );
4662 for (i, c) in doc_comments.drain(..).enumerate() {
4663 if let Some(comment) = c {
4664 ctx.module
4665 .get_or_insert_default_doc_comments()
4666 .struct_members
4667 .insert((handle, i), comment);
4668 }
4669 }
4670 Ok(handle)
4671 }
4672
4673 fn const_u32(
4674 &mut self,
4675 expr: Handle<ast::Expression<'source>>,
4676 ctx: &mut ExpressionContext<'source, '_, '_>,
4677 ) -> Result<'source, (u32, Span)> {
4678 let span = ctx.ast_expressions.get_span(expr);
4679 let expr = self.expression(expr, ctx)?;
4680 let value = ctx
4681 .module
4682 .to_ctx()
4683 .get_const_val(expr)
4684 .map_err(|err| match err {
4685 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4686 Error::ExpectedConstExprConcreteIntegerScalar(span)
4687 }
4688 proc::ConstValueError::Negative => Error::ExpectedNonNegative(span),
4689 })?;
4690 Ok((value, span))
4691 }
4692
4693 fn array_size(
4694 &mut self,
4695 expr: Handle<ast::Expression<'source>>,
4696 ctx: &mut ExpressionContext<'source, '_, '_>,
4697 ) -> Result<'source, ir::ArraySize> {
4698 let span = ctx.ast_expressions.get_span(expr);
4699 let const_ctx = &mut ctx.as_const();
4700 let const_expr = self.expression(expr, const_ctx);
4701 match const_expr {
4702 Ok(value) => {
4703 let len = const_ctx.get_const_val(value).map_err(|err| {
4704 Box::new(match err {
4705 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4706 Error::ExpectedConstExprConcreteIntegerScalar(span)
4707 }
4708 proc::ConstValueError::Negative => Error::ExpectedPositiveArrayLength(span),
4709 })
4710 })?;
4711 let size = NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;
4712 Ok(ir::ArraySize::Constant(size))
4713 }
4714 Err(err) => {
4715 let Error::ConstantEvaluatorError(ref ty, _) = *err else {
4718 return Err(err);
4719 };
4720
4721 let proc::ConstantEvaluatorError::OverrideExpr = **ty else {
4722 return Err(err);
4723 };
4724
4725 Ok(ir::ArraySize::Pending(self.array_size_override(
4726 expr,
4727 &mut ctx.as_global().as_override(),
4728 span,
4729 )?))
4730 }
4731 }
4732 }
4733
4734 fn array_size_override(
4735 &mut self,
4736 size_expr: Handle<ast::Expression<'source>>,
4737 ctx: &mut ExpressionContext<'source, '_, '_>,
4738 span: Span,
4739 ) -> Result<'source, Handle<ir::Override>> {
4740 let expr = self.expression(size_expr, ctx)?;
4741 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
4742 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({
4743 if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] {
4744 handle
4745 } else {
4746 let ty = ctx.register_type(expr)?;
4747 ctx.module.overrides.append(
4748 ir::Override {
4749 name: None,
4750 id: None,
4751 ty,
4752 init: Some(expr),
4753 },
4754 span,
4755 )
4756 }
4757 }),
4758 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
4759 span,
4760 ))),
4761 }
4762 }
4763
4764 fn resolve_named_ast_type(
4774 &mut self,
4775 ident: &ast::TemplateElaboratedIdent<'source>,
4776 name: String,
4777 ctx: &mut ExpressionContext<'source, '_, '_>,
4778 ) -> Result<'source, Handle<ir::Type>> {
4779 self.type_specifier(ident, ctx, Some(name))
4780 }
4781
4782 fn resolve_ast_type(
4784 &mut self,
4785 ident: &ast::TemplateElaboratedIdent<'source>,
4786 ctx: &mut ExpressionContext<'source, '_, '_>,
4787 ) -> Result<'source, Handle<ir::Type>> {
4788 self.type_specifier(ident, ctx, None)
4789 }
4790
4791 fn binding(
4792 &mut self,
4793 binding: &Option<ast::Binding<'source>>,
4794 ty: Handle<ir::Type>,
4795 ctx: &mut GlobalContext<'source, '_, '_>,
4796 ) -> Result<'source, Option<ir::Binding>> {
4797 Ok(match *binding {
4798 Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)),
4799 Some(ast::Binding::Location {
4800 location,
4801 interpolation,
4802 sampling,
4803 blend_src,
4804 per_primitive,
4805 }) => {
4806 let blend_src = if let Some(blend_src) = blend_src {
4807 Some(self.const_u32(blend_src, &mut ctx.as_const())?.0)
4808 } else {
4809 None
4810 };
4811
4812 let mut binding = ir::Binding::Location {
4813 location: self.const_u32(location, &mut ctx.as_const())?.0,
4814 interpolation,
4815 sampling,
4816 blend_src,
4817 per_primitive,
4818 };
4819 binding.apply_default_interpolation(&ctx.module.types[ty].inner);
4820 Some(binding)
4821 }
4822 None => None,
4823 })
4824 }
4825
4826 fn ray_query_pointer(
4827 &mut self,
4828 expr: Handle<ast::Expression<'source>>,
4829 ctx: &mut ExpressionContext<'source, '_, '_>,
4830 ) -> Result<'source, Handle<ir::Expression>> {
4831 let span = ctx.ast_expressions.get_span(expr);
4832 let pointer = self.expression(expr, ctx)?;
4833
4834 match *resolve_inner!(ctx, pointer) {
4835 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4836 ir::TypeInner::RayQuery { .. } => Ok(pointer),
4837 ref other => {
4838 log::error!("Pointer type to {other:?} passed to ray query op");
4839 Err(Box::new(Error::InvalidRayQueryPointer(span)))
4840 }
4841 },
4842 ref other => {
4843 log::error!("Type {other:?} passed to ray query op");
4844 Err(Box::new(Error::InvalidRayQueryPointer(span)))
4845 }
4846 }
4847 }
4848}
4849
4850impl ir::AtomicFunction {
4851 pub fn map(word: &str) -> Option<Self> {
4852 Some(match word {
4853 "atomicAdd" => ir::AtomicFunction::Add,
4854 "atomicSub" => ir::AtomicFunction::Subtract,
4855 "atomicAnd" => ir::AtomicFunction::And,
4856 "atomicOr" => ir::AtomicFunction::InclusiveOr,
4857 "atomicXor" => ir::AtomicFunction::ExclusiveOr,
4858 "atomicMin" => ir::AtomicFunction::Min,
4859 "atomicMax" => ir::AtomicFunction::Max,
4860 "atomicExchange" => ir::AtomicFunction::Exchange { compare: None },
4861 _ => return None,
4862 })
4863 }
4864}