1use alloc::{
2 borrow::ToOwned,
3 boxed::Box,
4 format,
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::{matches, 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 span = self.get_expression_span(pointer);
905
906 if let ir::TypeInner::Pointer { base, .. } = *resolve_inner!(self, pointer) {
909 fn type_contains_atomic(
910 ty: Handle<ir::Type>,
911 types: &crate::UniqueArena<ir::Type>,
912 ) -> bool {
913 match types[ty].inner {
914 ir::TypeInner::Atomic(_) => true,
915 ir::TypeInner::Array { base, .. } => type_contains_atomic(base, types),
916 ir::TypeInner::Struct { ref members, .. } => members
917 .iter()
918 .any(|member| type_contains_atomic(member.ty, types)),
919 _ => false,
920 }
921 }
922
923 if type_contains_atomic(base, &self.module.types) {
924 return Err(Box::new(Error::InvalidAtomicAccess(span)));
925 }
926 }
927
928 let load = ir::Expression::Load { pointer };
929 self.append_expression(load, span)
930 }
931 Typed::Plain(handle) => Ok(handle),
932 }
933 }
934
935 fn ensure_type_exists(&mut self, inner: ir::TypeInner) -> Handle<ir::Type> {
936 self.as_global().ensure_type_exists(None, inner)
937 }
938
939 fn enumerant(
950 &self,
951 expr: Handle<ast::Expression<'source>>,
952 ) -> Result<'source, (&'source str, Span)> {
953 let span = self.ast_expressions.get_span(expr);
954 let expr = &self.ast_expressions[expr];
955
956 let ast::Expression::Ident(ref ident) = *expr else {
957 return Err(Box::new(Error::UnexpectedExprForEnumerant(span)));
958 };
959
960 let ast::TemplateElaboratedIdent {
961 ident: ast::IdentExpr::Unresolved(name),
962 ref template_list,
963 ..
964 } = *ident
965 else {
966 return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));
967 };
968
969 if self.globals.get(name).is_some() {
970 return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));
971 }
972
973 if !template_list.is_empty() {
974 return Err(Box::new(Error::UnexpectedTemplate(span)));
975 }
976
977 Ok((name, span))
978 }
979
980 fn var_address_space(
981 &self,
982 template_list: &[Handle<ast::Expression<'source>>],
983 ) -> Result<'source, ir::AddressSpace> {
984 let mut tl = TemplateListIter::new(Span::UNDEFINED, template_list);
985 let mut address_space = tl.maybe_address_space(self)?;
986 if let Some(ref mut address_space) = address_space {
987 tl.maybe_access_mode(address_space, self)?;
988 }
989 tl.finish(self)?;
990 Ok(address_space.unwrap_or(ir::AddressSpace::Handle))
991 }
992}
993
994struct ArgumentContext<'ctx, 'source> {
995 args: core::slice::Iter<'ctx, Handle<ast::Expression<'source>>>,
996 min_args: u32,
997 args_used: u32,
998 total_args: u32,
999 span: Span,
1000}
1001
1002impl<'source> ArgumentContext<'_, 'source> {
1003 pub fn finish(self) -> Result<'source, ()> {
1004 if self.args.len() == 0 {
1005 Ok(())
1006 } else {
1007 Err(Box::new(Error::WrongArgumentCount {
1008 found: self.total_args,
1009 expected: self.min_args..self.args_used + 1,
1010 span: self.span,
1011 }))
1012 }
1013 }
1014
1015 pub fn next(&mut self) -> Result<'source, Handle<ast::Expression<'source>>> {
1016 match self.args.next().copied() {
1017 Some(arg) => {
1018 self.args_used += 1;
1019 Ok(arg)
1020 }
1021 None => Err(Box::new(Error::WrongArgumentCount {
1022 found: self.total_args,
1023 expected: self.min_args..self.args_used + 1,
1024 span: self.span,
1025 })),
1026 }
1027 }
1028}
1029
1030#[derive(Debug, Copy, Clone)]
1031enum Declared<T> {
1032 Const(T),
1034
1035 Runtime(T),
1037}
1038
1039impl<T> Declared<T> {
1040 fn runtime(self) -> T {
1041 match self {
1042 Declared::Const(t) | Declared::Runtime(t) => t,
1043 }
1044 }
1045
1046 fn const_time(self) -> Option<T> {
1047 match self {
1048 Declared::Const(t) => Some(t),
1049 Declared::Runtime(_) => None,
1050 }
1051 }
1052}
1053
1054#[derive(Debug, Copy, Clone)]
1076enum Typed<T> {
1077 Reference(T),
1079
1080 Plain(T),
1082}
1083
1084impl<T> Typed<T> {
1085 fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> {
1086 match self {
1087 Self::Reference(v) => Typed::Reference(f(v)),
1088 Self::Plain(v) => Typed::Plain(f(v)),
1089 }
1090 }
1091
1092 fn try_map<U, E>(
1093 self,
1094 mut f: impl FnMut(T) -> core::result::Result<U, E>,
1095 ) -> core::result::Result<Typed<U>, E> {
1096 Ok(match self {
1097 Self::Reference(expr) => Typed::Reference(f(expr)?),
1098 Self::Plain(expr) => Typed::Plain(f(expr)?),
1099 })
1100 }
1101
1102 fn ref_or<E>(self, error: E) -> core::result::Result<T, E> {
1103 match self {
1104 Self::Reference(v) => Ok(v),
1105 Self::Plain(_) => Err(error),
1106 }
1107 }
1108}
1109
1110enum Components {
1116 Single(u32),
1117 Swizzle {
1118 size: ir::VectorSize,
1119 pattern: [ir::SwizzleComponent; 4],
1120 },
1121}
1122
1123impl Components {
1124 const fn letter_component(letter: char) -> Option<ir::SwizzleComponent> {
1125 use ir::SwizzleComponent as Sc;
1126 match letter {
1127 'x' | 'r' => Some(Sc::X),
1128 'y' | 'g' => Some(Sc::Y),
1129 'z' | 'b' => Some(Sc::Z),
1130 'w' | 'a' => Some(Sc::W),
1131 _ => None,
1132 }
1133 }
1134
1135 fn single_component(name: &str, name_span: Span) -> Result<'_, u32> {
1136 let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?;
1137 match Self::letter_component(ch) {
1138 Some(sc) => Ok(sc as u32),
1139 None => Err(Box::new(Error::BadAccessor(name_span))),
1140 }
1141 }
1142
1143 fn new(name: &str, name_span: Span) -> Result<'_, Self> {
1147 let size = match name.len() {
1148 1 => return Ok(Components::Single(Self::single_component(name, name_span)?)),
1149 2 => ir::VectorSize::Bi,
1150 3 => ir::VectorSize::Tri,
1151 4 => ir::VectorSize::Quad,
1152 _ => return Err(Box::new(Error::BadAccessor(name_span))),
1153 };
1154
1155 let mut pattern = [ir::SwizzleComponent::X; 4];
1156 for (comp, ch) in pattern.iter_mut().zip(name.chars()) {
1157 *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?;
1158 }
1159
1160 if name.chars().all(|c| matches!(c, 'x' | 'y' | 'z' | 'w'))
1161 || name.chars().all(|c| matches!(c, 'r' | 'g' | 'b' | 'a'))
1162 {
1163 Ok(Components::Swizzle { size, pattern })
1164 } else {
1165 Err(Box::new(Error::BadAccessor(name_span)))
1166 }
1167 }
1168}
1169
1170enum LoweredGlobalDecl {
1172 Function {
1173 handle: Handle<ir::Function>,
1174 must_use: bool,
1175 },
1176 Var(Handle<ir::GlobalVariable>),
1177 Const(Handle<ir::Constant>),
1178 Override(Handle<ir::Override>),
1179 Type(Handle<ir::Type>),
1180 EntryPoint(usize),
1181}
1182
1183enum Texture {
1184 Gather,
1185 GatherCompare,
1186
1187 Sample,
1188 SampleBias,
1189 SampleCompare,
1190 SampleCompareLevel,
1191 SampleGrad,
1192 SampleLevel,
1193 SampleBaseClampToEdge,
1194}
1195
1196impl Texture {
1197 pub fn map(word: &str) -> Option<Self> {
1198 Some(match word {
1199 "textureGather" => Self::Gather,
1200 "textureGatherCompare" => Self::GatherCompare,
1201
1202 "textureSample" => Self::Sample,
1203 "textureSampleBias" => Self::SampleBias,
1204 "textureSampleCompare" => Self::SampleCompare,
1205 "textureSampleCompareLevel" => Self::SampleCompareLevel,
1206 "textureSampleGrad" => Self::SampleGrad,
1207 "textureSampleLevel" => Self::SampleLevel,
1208 "textureSampleBaseClampToEdge" => Self::SampleBaseClampToEdge,
1209 _ => return None,
1210 })
1211 }
1212
1213 pub const fn min_argument_count(&self) -> u32 {
1214 match *self {
1215 Self::Gather => 3,
1216 Self::GatherCompare => 4,
1217
1218 Self::Sample => 3,
1219 Self::SampleBias => 5,
1220 Self::SampleCompare => 5,
1221 Self::SampleCompareLevel => 5,
1222 Self::SampleGrad => 6,
1223 Self::SampleLevel => 5,
1224 Self::SampleBaseClampToEdge => 3,
1225 }
1226 }
1227}
1228
1229enum SubgroupGather {
1230 BroadcastFirst,
1231 Broadcast,
1232 Shuffle,
1233 ShuffleDown,
1234 ShuffleUp,
1235 ShuffleXor,
1236 QuadBroadcast,
1237}
1238
1239impl SubgroupGather {
1240 pub fn map(word: &str) -> Option<Self> {
1241 Some(match word {
1242 "subgroupBroadcastFirst" => Self::BroadcastFirst,
1243 "subgroupBroadcast" => Self::Broadcast,
1244 "subgroupShuffle" => Self::Shuffle,
1245 "subgroupShuffleDown" => Self::ShuffleDown,
1246 "subgroupShuffleUp" => Self::ShuffleUp,
1247 "subgroupShuffleXor" => Self::ShuffleXor,
1248 "quadBroadcast" => Self::QuadBroadcast,
1249 _ => return None,
1250 })
1251 }
1252}
1253
1254enum AbstractRule {
1256 Concretize,
1258
1259 Allow,
1261}
1262
1263#[derive(Debug, Copy, Clone)]
1265enum MustUse {
1266 Yes,
1267 No,
1268}
1269
1270impl From<bool> for MustUse {
1271 fn from(value: bool) -> Self {
1272 if value {
1273 MustUse::Yes
1274 } else {
1275 MustUse::No
1276 }
1277 }
1278}
1279
1280pub struct Lowerer<'source, 'temp> {
1281 index: &'temp Index<'source>,
1282}
1283
1284impl<'source, 'temp> Lowerer<'source, 'temp> {
1285 pub const fn new(index: &'temp Index<'source>) -> Self {
1286 Self { index }
1287 }
1288
1289 pub fn lower(&mut self, tu: ast::TranslationUnit<'source>) -> Result<'source, ir::Module> {
1290 let mut module = ir::Module {
1291 diagnostic_filters: tu.diagnostic_filters,
1292 diagnostic_filter_leaf: tu.diagnostic_filter_leaf,
1293 ..Default::default()
1294 };
1295
1296 let mut ctx = GlobalContext {
1297 enable_extensions: tu.enable_extensions,
1298 ast_expressions: &tu.expressions,
1299 globals: &mut FastHashMap::default(),
1300 module: &mut module,
1301 const_typifier: &mut Typifier::new(),
1302 layouter: &mut proc::Layouter::default(),
1303 global_expression_kind_tracker: &mut proc::ExpressionKindTracker::new(),
1304 };
1305 if !tu.doc_comments.is_empty() {
1306 ctx.module.get_or_insert_default_doc_comments().module =
1307 tu.doc_comments.iter().map(|s| s.to_string()).collect();
1308 }
1309
1310 for decl_handle in self.index.visit_ordered() {
1311 let span = tu.decls.get_span(decl_handle);
1312 let decl = &tu.decls[decl_handle];
1313
1314 match decl.kind {
1315 ast::GlobalDeclKind::Fn(ref f) => {
1316 let lowered_decl = self.function(f, span, &mut ctx)?;
1317 if !f.doc_comments.is_empty() {
1318 match lowered_decl {
1319 LoweredGlobalDecl::Function { handle, .. } => {
1320 ctx.module
1321 .get_or_insert_default_doc_comments()
1322 .functions
1323 .insert(
1324 handle,
1325 f.doc_comments.iter().map(|s| s.to_string()).collect(),
1326 );
1327 }
1328 LoweredGlobalDecl::EntryPoint(index) => {
1329 ctx.module
1330 .get_or_insert_default_doc_comments()
1331 .entry_points
1332 .insert(
1333 index,
1334 f.doc_comments.iter().map(|s| s.to_string()).collect(),
1335 );
1336 }
1337 _ => {}
1338 }
1339 }
1340 ctx.globals.insert(f.name.name, lowered_decl);
1341 }
1342 ast::GlobalDeclKind::Var(ref v) => {
1343 let explicit_ty =
1344 v.ty.as_ref()
1345 .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1346 .transpose()?;
1347
1348 let (ty, initializer) = self.type_and_init(
1349 v.name,
1350 v.init,
1351 explicit_ty,
1352 AbstractRule::Concretize,
1353 &mut ctx.as_override(),
1354 )?;
1355
1356 let binding = if let Some(ref binding) = v.binding {
1357 Some(ir::ResourceBinding {
1358 group: self.const_u32(binding.group, &mut ctx.as_const())?.0,
1359 binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0,
1360 })
1361 } else {
1362 None
1363 };
1364
1365 let space = ctx.as_const().var_address_space(&v.template_list)?;
1366
1367 let handle = ctx.module.global_variables.append(
1368 ir::GlobalVariable {
1369 name: Some(v.name.name.to_string()),
1370 space,
1371 binding,
1372 ty,
1373 init: initializer,
1374 memory_decorations: v.memory_decorations,
1375 },
1376 span,
1377 );
1378
1379 if !v.doc_comments.is_empty() {
1380 ctx.module
1381 .get_or_insert_default_doc_comments()
1382 .global_variables
1383 .insert(
1384 handle,
1385 v.doc_comments.iter().map(|s| s.to_string()).collect(),
1386 );
1387 }
1388 ctx.globals
1389 .insert(v.name.name, LoweredGlobalDecl::Var(handle));
1390 }
1391 ast::GlobalDeclKind::Const(ref c) => {
1392 let mut ectx = ctx.as_const();
1393
1394 let explicit_ty =
1395 c.ty.as_ref()
1396 .map(|ast| self.resolve_ast_type(ast, &mut ectx))
1397 .transpose()?;
1398
1399 let (ty, init) = self.type_and_init(
1400 c.name,
1401 Some(c.init),
1402 explicit_ty,
1403 AbstractRule::Allow,
1404 &mut ectx,
1405 )?;
1406 let init = init.expect("Global const must have init");
1407
1408 let handle = ctx.module.constants.append(
1409 ir::Constant {
1410 name: Some(c.name.name.to_string()),
1411 ty,
1412 init,
1413 },
1414 span,
1415 );
1416
1417 ctx.globals
1418 .insert(c.name.name, LoweredGlobalDecl::Const(handle));
1419 if !c.doc_comments.is_empty() {
1420 ctx.module
1421 .get_or_insert_default_doc_comments()
1422 .constants
1423 .insert(
1424 handle,
1425 c.doc_comments.iter().map(|s| s.to_string()).collect(),
1426 );
1427 }
1428 }
1429 ast::GlobalDeclKind::Override(ref o) => {
1430 let explicit_ty =
1431 o.ty.as_ref()
1432 .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1433 .transpose()?;
1434
1435 let mut ectx = ctx.as_override();
1436
1437 let (ty, init) = self.type_and_init(
1438 o.name,
1439 o.init,
1440 explicit_ty,
1441 AbstractRule::Concretize,
1442 &mut ectx,
1443 )?;
1444
1445 let id =
1446 o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))
1447 .transpose()?;
1448
1449 let id = if let Some((id, id_span)) = id {
1450 Some(
1451 u16::try_from(id)
1452 .map_err(|_| Error::PipelineConstantIDValue(id_span))?,
1453 )
1454 } else {
1455 None
1456 };
1457
1458 let handle = ctx.module.overrides.append(
1459 ir::Override {
1460 name: Some(o.name.name.to_string()),
1461 id,
1462 ty,
1463 init,
1464 },
1465 span,
1466 );
1467
1468 ctx.globals
1469 .insert(o.name.name, LoweredGlobalDecl::Override(handle));
1470 }
1471 ast::GlobalDeclKind::Struct(ref s) => {
1472 let handle = self.r#struct(s, span, &mut ctx)?;
1473 ctx.globals
1474 .insert(s.name.name, LoweredGlobalDecl::Type(handle));
1475 if !s.doc_comments.is_empty() {
1476 ctx.module
1477 .get_or_insert_default_doc_comments()
1478 .types
1479 .insert(
1480 handle,
1481 s.doc_comments.iter().map(|s| s.to_string()).collect(),
1482 );
1483 }
1484 }
1485 ast::GlobalDeclKind::Type(ref alias) => {
1486 let ty = self.resolve_named_ast_type(
1487 &alias.ty,
1488 alias.name.name.to_string(),
1489 &mut ctx.as_const(),
1490 )?;
1491 ctx.globals
1492 .insert(alias.name.name, LoweredGlobalDecl::Type(ty));
1493 }
1494 ast::GlobalDeclKind::ConstAssert(condition) => {
1495 let condition = self.expression(condition, &mut ctx.as_const())?;
1496
1497 let span = ctx.module.global_expressions.get_span(condition);
1498 match ctx
1499 .module
1500 .to_ctx()
1501 .get_const_val_from(condition, &ctx.module.global_expressions)
1502 {
1503 Ok(true) => Ok(()),
1504 Ok(false) => Err(Error::ConstAssertFailed(span)),
1505 Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
1506 unreachable!()
1507 }
1508 Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
1509 }?;
1510 }
1511 }
1512 }
1513
1514 crate::compact::compact(&mut module, KeepUnused::Yes);
1518
1519 Ok(module)
1520 }
1521
1522 fn type_and_init(
1524 &mut self,
1525 name: ast::Ident<'source>,
1526 init: Option<Handle<ast::Expression<'source>>>,
1527 explicit_ty: Option<Handle<ir::Type>>,
1528 abstract_rule: AbstractRule,
1529 ectx: &mut ExpressionContext<'source, '_, '_>,
1530 ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> {
1531 let ty;
1532 let initializer;
1533 match (init, explicit_ty) {
1534 (Some(init), Some(explicit_ty)) => {
1535 let init = self.expression_for_abstract(init, ectx)?;
1536 let ty_res = proc::TypeResolution::Handle(explicit_ty);
1537 let init = ectx
1538 .try_automatic_conversions(init, &ty_res, name.span)
1539 .map_err(|error| match *error {
1540 Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch {
1544 name: name.span,
1545 expected: e.dest_type,
1546 got: e.source_type,
1547 }),
1548 Error::TypeMismatch(e) => Box::new(Error::InitializationTypeMismatch {
1549 name: name.span,
1550 expected: e.dest_type,
1551 got: e.source_type,
1552 }),
1553 _ => error,
1554 })?;
1555
1556 ty = explicit_ty;
1557 initializer = Some(init);
1558 }
1559 (Some(init), None) => {
1560 let mut init = self.expression_for_abstract(init, ectx)?;
1561 if let AbstractRule::Concretize = abstract_rule {
1562 init = ectx.concretize(init)?;
1563 }
1564 ty = ectx.register_type(init)?;
1565 initializer = Some(init);
1566 }
1567 (None, Some(explicit_ty)) => {
1568 ty = explicit_ty;
1569 initializer = None;
1570 }
1571 (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))),
1572 }
1573 Ok((ty, initializer))
1574 }
1575
1576 fn function(
1577 &mut self,
1578 f: &ast::Function<'source>,
1579 span: Span,
1580 ctx: &mut GlobalContext<'source, '_, '_>,
1581 ) -> Result<'source, LoweredGlobalDecl> {
1582 let mut local_table = FastHashMap::default();
1583 let mut expressions = Arena::new();
1584 let mut named_expressions = FastIndexMap::default();
1585 let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new();
1586
1587 let arguments = f
1588 .arguments
1589 .iter()
1590 .enumerate()
1591 .map(|(i, arg)| -> Result<'_, _> {
1592 let ty = self.resolve_ast_type(&arg.ty, &mut ctx.as_const())?;
1593 let expr =
1594 expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span);
1595 local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr)));
1596 named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));
1597 local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime);
1598
1599 Ok(ir::FunctionArgument {
1600 name: Some(arg.name.name.to_string()),
1601 ty,
1602 binding: self.binding(&arg.binding, ty, ctx)?,
1603 })
1604 })
1605 .collect::<Result<Vec<_>>>()?;
1606
1607 let result = f
1608 .result
1609 .as_ref()
1610 .map(|res| -> Result<'_, _> {
1611 let ty = self.resolve_ast_type(&res.ty, &mut ctx.as_const())?;
1612 Ok(ir::FunctionResult {
1613 ty,
1614 binding: self.binding(&res.binding, ty, ctx)?,
1615 })
1616 })
1617 .transpose()?;
1618
1619 let mut function = ir::Function {
1620 name: Some(f.name.name.to_string()),
1621 arguments,
1622 result,
1623 local_variables: Arena::new(),
1624 expressions,
1625 named_expressions: crate::NamedExpressions::default(),
1626 body: ir::Block::default(),
1627 diagnostic_filter_leaf: f.diagnostic_filter_leaf,
1628 };
1629
1630 let mut typifier = Typifier::default();
1631 let mut stmt_ctx = StatementContext {
1632 enable_extensions: ctx.enable_extensions,
1633 local_table: &mut local_table,
1634 globals: ctx.globals,
1635 ast_expressions: ctx.ast_expressions,
1636 const_typifier: ctx.const_typifier,
1637 typifier: &mut typifier,
1638 layouter: ctx.layouter,
1639 function: &mut function,
1640 named_expressions: &mut named_expressions,
1641 module: ctx.module,
1642 local_expression_kind_tracker: &mut local_expression_kind_tracker,
1643 global_expression_kind_tracker: ctx.global_expression_kind_tracker,
1644 };
1645 let mut body = self.block(&f.body, false, &mut stmt_ctx)?;
1646 proc::ensure_block_returns(&mut body);
1647
1648 function.body = body;
1649 function.named_expressions = named_expressions
1650 .into_iter()
1651 .map(|(key, (name, _))| (key, name))
1652 .collect();
1653
1654 if let Some(ref entry) = f.entry_point {
1655 let (workgroup_size, workgroup_size_overrides) =
1656 if let Some(workgroup_size) = entry.workgroup_size {
1657 let mut workgroup_size_out = [1; 3];
1659 let mut workgroup_size_overrides_out = [None; 3];
1660 for (i, size) in workgroup_size.into_iter().enumerate() {
1661 if let Some(size_expr) = size {
1662 match self.const_u32(size_expr, &mut ctx.as_const()) {
1663 Ok(value) => {
1664 workgroup_size_out[i] = value.0;
1665 }
1666 Err(err) => {
1667 if let Error::ConstantEvaluatorError(ref ty, _) = *err {
1668 match **ty {
1669 proc::ConstantEvaluatorError::OverrideExpr => {
1670 workgroup_size_overrides_out[i] =
1671 Some(self.workgroup_size_override(
1672 size_expr,
1673 &mut ctx.as_override(),
1674 )?);
1675 }
1676 _ => {
1677 return Err(err);
1678 }
1679 }
1680 } else {
1681 return Err(err);
1682 }
1683 }
1684 }
1685 }
1686 }
1687 if workgroup_size_overrides_out.iter().all(|x| x.is_none()) {
1688 (workgroup_size_out, None)
1689 } else {
1690 (workgroup_size_out, Some(workgroup_size_overrides_out))
1691 }
1692 } else {
1693 ([0; 3], None)
1694 };
1695
1696 let mesh_info = if let Some((var_name, var_span)) = entry.mesh_output_variable {
1697 let var = match ctx.globals.get(var_name) {
1698 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1699 Some(_) => {
1700 return Err(Box::new(Error::ExpectedGlobalVariable {
1701 name_span: var_span,
1702 }))
1703 }
1704 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1705 };
1706
1707 let mut info = ctx.module.analyze_mesh_shader_info(var);
1708 if let Some(h) = info.1[0] {
1709 info.0.max_vertices_override = Some(
1710 ctx.module
1711 .global_expressions
1712 .append(crate::Expression::Override(h), Span::UNDEFINED),
1713 );
1714 }
1715 if let Some(h) = info.1[1] {
1716 info.0.max_primitives_override = Some(
1717 ctx.module
1718 .global_expressions
1719 .append(crate::Expression::Override(h), Span::UNDEFINED),
1720 );
1721 }
1722
1723 Some(info.0)
1724 } else {
1725 None
1726 };
1727
1728 let task_payload = if let Some((var_name, var_span)) = entry.task_payload {
1729 Some(match ctx.globals.get(var_name) {
1730 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1731 Some(_) => {
1732 return Err(Box::new(Error::ExpectedGlobalVariable {
1733 name_span: var_span,
1734 }))
1735 }
1736 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1737 })
1738 } else {
1739 None
1740 };
1741
1742 let incoming_ray_payload =
1743 if let Some((var_name, var_span)) = entry.ray_incoming_payload {
1744 Some(match ctx.globals.get(var_name) {
1745 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1746 Some(_) => {
1747 return Err(Box::new(Error::ExpectedGlobalVariable {
1748 name_span: var_span,
1749 }))
1750 }
1751 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1752 })
1753 } else {
1754 None
1755 };
1756
1757 ctx.module.entry_points.push(ir::EntryPoint {
1758 name: f.name.name.to_string(),
1759 stage: entry.stage,
1760 early_depth_test: entry.early_depth_test,
1761 workgroup_size,
1762 workgroup_size_overrides,
1763 function,
1764 mesh_info,
1765 task_payload,
1766 incoming_ray_payload,
1767 });
1768 Ok(LoweredGlobalDecl::EntryPoint(
1769 ctx.module.entry_points.len() - 1,
1770 ))
1771 } else {
1772 let handle = ctx.module.functions.append(function, span);
1773 Ok(LoweredGlobalDecl::Function {
1774 handle,
1775 must_use: f.result.as_ref().is_some_and(|res| res.must_use),
1776 })
1777 }
1778 }
1779
1780 fn workgroup_size_override(
1781 &mut self,
1782 size_expr: Handle<ast::Expression<'source>>,
1783 ctx: &mut ExpressionContext<'source, '_, '_>,
1784 ) -> Result<'source, Handle<ir::Expression>> {
1785 let span = ctx.ast_expressions.get_span(size_expr);
1786 let expr = self.expression(size_expr, ctx)?;
1787 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
1788 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr),
1789 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
1790 span,
1791 ))),
1792 }
1793 }
1794
1795 fn block(
1796 &mut self,
1797 b: &ast::Block<'source>,
1798 is_inside_loop: bool,
1799 ctx: &mut StatementContext<'source, '_, '_>,
1800 ) -> Result<'source, ir::Block> {
1801 let mut block = ir::Block::default();
1802
1803 for stmt in b.stmts.iter() {
1804 self.statement(stmt, &mut block, is_inside_loop, ctx)?;
1805 }
1806
1807 Ok(block)
1808 }
1809
1810 fn statement(
1811 &mut self,
1812 stmt: &ast::Statement<'source>,
1813 block: &mut ir::Block,
1814 is_inside_loop: bool,
1815 ctx: &mut StatementContext<'source, '_, '_>,
1816 ) -> Result<'source, ()> {
1817 let out = match stmt.kind {
1818 ast::StatementKind::Block(ref block) => {
1819 let block = self.block(block, is_inside_loop, ctx)?;
1820 ir::Statement::Block(block)
1821 }
1822 ast::StatementKind::LocalDecl(ref decl) => match *decl {
1823 ast::LocalDecl::Let(ref l) => {
1824 let mut emitter = proc::Emitter::default();
1825 emitter.start(&ctx.function.expressions);
1826
1827 let explicit_ty = l
1828 .ty
1829 .as_ref()
1830 .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter)))
1831 .transpose()?;
1832
1833 let mut ectx = ctx.as_expression(block, &mut emitter);
1834
1835 let (ty, initializer) = self.type_and_init(
1836 l.name,
1837 Some(l.init),
1838 explicit_ty,
1839 AbstractRule::Concretize,
1840 &mut ectx,
1841 )?;
1842
1843 if ctx.module.types[ty]
1851 .inner
1852 .is_dynamically_sized(&ctx.module.types)
1853 {
1854 return Err(Box::new(Error::TypeNotConstructible(l.name.span)));
1855 }
1856
1857 let initializer =
1860 initializer.expect("type_and_init did not return an initializer");
1861
1862 ctx.local_expression_kind_tracker
1868 .force_non_const(initializer);
1869
1870 block.extend(emitter.finish(&ctx.function.expressions));
1871 ctx.local_table
1872 .insert(l.handle, Declared::Runtime(Typed::Plain(initializer)));
1873 ctx.named_expressions
1874 .insert(initializer, (l.name.name.to_string(), l.name.span));
1875
1876 if matches!(
1877 ctx.module.types[ty].inner,
1878 crate::TypeInner::RayQuery { .. }
1879 ) {
1880 return Err(Box::new(Error::RayQueryWithInitializer(
1884 ctx.function.expressions.get_span(initializer),
1885 )));
1886 }
1887
1888 return Ok(());
1889 }
1890 ast::LocalDecl::Var(ref v) => {
1891 let mut emitter = proc::Emitter::default();
1892 emitter.start(&ctx.function.expressions);
1893
1894 let explicit_ty =
1895 v.ty.as_ref()
1896 .map(|ast| {
1897 self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter))
1898 })
1899 .transpose()?;
1900
1901 let mut ectx = ctx.as_expression(block, &mut emitter);
1902 let (ty, initializer) = self.type_and_init(
1903 v.name,
1904 v.init,
1905 explicit_ty,
1906 AbstractRule::Concretize,
1907 &mut ectx,
1908 )?;
1909
1910 let (const_initializer, initializer) = {
1911 match initializer {
1912 Some(init) => {
1913 if is_inside_loop
1921 || !ctx.local_expression_kind_tracker.is_const_or_override(init)
1922 {
1923 (None, Some(init))
1924 } else {
1925 (Some(init), None)
1926 }
1927 }
1928 None => (None, None),
1929 }
1930 };
1931
1932 let var = ctx.function.local_variables.append(
1933 ir::LocalVariable {
1934 name: Some(v.name.name.to_string()),
1935 ty,
1936 init: const_initializer,
1937 },
1938 stmt.span,
1939 );
1940
1941 let handle = ctx
1942 .as_expression(block, &mut emitter)
1943 .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?;
1944
1945 block.extend(emitter.finish(&ctx.function.expressions));
1946 ctx.local_table
1947 .insert(v.handle, Declared::Runtime(Typed::Reference(handle)));
1948
1949 match ctx.module.types[ty].inner {
1950 crate::TypeInner::RayQuery { .. } => {
1951 if let Some(expr) = initializer {
1960 return Err(Box::new(Error::RayQueryWithInitializer(
1961 ctx.function.expressions.get_span(expr),
1962 )));
1963 }
1964
1965 if is_inside_loop {
1966 ir::Statement::RayQuery {
1967 query: handle,
1968 fun: ir::RayQueryFunction::Begin,
1969 }
1970 } else {
1971 return Ok(());
1972 }
1973 }
1974 _ => {
1975 let initializer = if is_inside_loop {
1976 match initializer {
1977 Some(initializer) => Some(initializer),
1978 None => Some(
1979 ctx.as_expression(block, &mut emitter).append_expression(
1980 ir::Expression::ZeroValue(ty),
1981 stmt.span,
1982 )?,
1983 ),
1984 }
1985 } else {
1986 initializer
1987 };
1988
1989 match initializer {
1990 Some(initializer) => ir::Statement::Store {
1991 pointer: handle,
1992 value: initializer,
1993 },
1994 None => return Ok(()),
1995 }
1996 }
1997 }
1998 }
1999 ast::LocalDecl::Const(ref c) => {
2000 let mut emitter = proc::Emitter::default();
2001 emitter.start(&ctx.function.expressions);
2002
2003 let ectx = &mut ctx.as_const(block, &mut emitter);
2004
2005 let explicit_ty =
2006 c.ty.as_ref()
2007 .map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const()))
2008 .transpose()?;
2009
2010 let (_ty, init) = self.type_and_init(
2011 c.name,
2012 Some(c.init),
2013 explicit_ty,
2014 AbstractRule::Allow,
2015 &mut ectx.as_const(),
2016 )?;
2017 let init = init.expect("Local const must have init");
2018
2019 block.extend(emitter.finish(&ctx.function.expressions));
2020 ctx.local_table
2021 .insert(c.handle, Declared::Const(Typed::Plain(init)));
2022 return Ok(());
2023 }
2024 },
2025 ast::StatementKind::If {
2026 condition,
2027 ref accept,
2028 ref reject,
2029 } => {
2030 let mut emitter = proc::Emitter::default();
2031 emitter.start(&ctx.function.expressions);
2032
2033 let condition =
2034 self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;
2035 block.extend(emitter.finish(&ctx.function.expressions));
2036
2037 let accept = self.block(accept, is_inside_loop, ctx)?;
2038 let reject = self.block(reject, is_inside_loop, ctx)?;
2039
2040 ir::Statement::If {
2041 condition,
2042 accept,
2043 reject,
2044 }
2045 }
2046 ast::StatementKind::Switch {
2047 selector,
2048 ref cases,
2049 } => {
2050 let mut emitter = proc::Emitter::default();
2051 emitter.start(&ctx.function.expressions);
2052
2053 let mut ectx = ctx.as_expression(block, &mut emitter);
2054
2055 let (mut exprs, spans) = core::iter::once(selector)
2058 .chain(cases.iter().filter_map(|case| match case.value {
2059 ast::SwitchValue::Expr(expr) => Some(expr),
2060 ast::SwitchValue::Default => None,
2061 }))
2062 .enumerate()
2063 .map(|(i, expr)| {
2064 let span = ectx.ast_expressions.get_span(expr);
2065 let expr = self.expression_for_abstract(expr, &mut ectx)?;
2066 let ty = resolve_inner!(ectx, expr);
2067 match *ty {
2068 ir::TypeInner::Scalar(
2069 ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT,
2070 ) => Ok((expr, span)),
2071 _ => match i {
2072 0 => Err(Box::new(Error::InvalidSwitchSelector { span })),
2073 _ => Err(Box::new(Error::InvalidSwitchCase { span })),
2074 },
2075 }
2076 })
2077 .collect::<Result<(Vec<_>, Vec<_>)>>()?;
2078
2079 let mut consensus =
2080 ectx.automatic_conversion_consensus(None, &exprs)
2081 .map_err(|span_idx| Error::SwitchCaseTypeMismatch {
2082 span: spans[span_idx],
2083 })?;
2084 if consensus == ir::Scalar::ABSTRACT_INT {
2086 consensus = ir::Scalar::I32;
2087 }
2088 for expr in &mut exprs {
2089 ectx.convert_to_leaf_scalar(expr, consensus)?;
2090 }
2091
2092 block.extend(emitter.finish(&ctx.function.expressions));
2093
2094 let mut exprs = exprs.into_iter();
2095 let selector = exprs
2096 .next()
2097 .expect("First element should be selector expression");
2098
2099 let cases = cases
2100 .iter()
2101 .map(|case| {
2102 Ok(ir::SwitchCase {
2103 value: match case.value {
2104 ast::SwitchValue::Expr(expr) => {
2105 let span = ctx.ast_expressions.get_span(expr);
2106 let expr = exprs.next().expect(
2107 "Should yield expression for each SwitchValue::Expr case",
2108 );
2109 match ctx
2110 .module
2111 .to_ctx()
2112 .get_const_val_from(expr, &ctx.function.expressions)
2113 {
2114 Ok(ir::Literal::I32(value)) => ir::SwitchValue::I32(value),
2115 Ok(ir::Literal::U32(value)) => ir::SwitchValue::U32(value),
2116 _ => {
2117 return Err(Box::new(Error::InvalidSwitchCase {
2118 span,
2119 }));
2120 }
2121 }
2122 }
2123 ast::SwitchValue::Default => ir::SwitchValue::Default,
2124 },
2125 body: self.block(&case.body, is_inside_loop, ctx)?,
2126 fall_through: case.fall_through,
2127 })
2128 })
2129 .collect::<Result<_>>()?;
2130
2131 ir::Statement::Switch { selector, cases }
2132 }
2133 ast::StatementKind::Loop {
2134 ref body,
2135 ref continuing,
2136 break_if,
2137 } => {
2138 let body = self.block(body, true, ctx)?;
2139 let mut continuing = self.block(continuing, true, ctx)?;
2140
2141 let mut emitter = proc::Emitter::default();
2142 emitter.start(&ctx.function.expressions);
2143 let break_if = break_if
2144 .map(|expr| {
2145 self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))
2146 })
2147 .transpose()?;
2148 continuing.extend(emitter.finish(&ctx.function.expressions));
2149
2150 ir::Statement::Loop {
2151 body,
2152 continuing,
2153 break_if,
2154 }
2155 }
2156 ast::StatementKind::Break => ir::Statement::Break,
2157 ast::StatementKind::Continue => ir::Statement::Continue,
2158 ast::StatementKind::Return { value: ast_value } => {
2159 let mut emitter = proc::Emitter::default();
2160 emitter.start(&ctx.function.expressions);
2161
2162 let value;
2163 if let Some(ast_expr) = ast_value {
2164 let value_span = ctx.ast_expressions.get_span(ast_expr);
2165 let result_ty = ctx.function.result.as_ref().map(|r| r.ty);
2166 let mut ectx = ctx.as_expression(block, &mut emitter);
2167 let expr = self.expression_for_abstract(ast_expr, &mut ectx)?;
2168
2169 if let Some(result_ty) = result_ty {
2170 let mut ectx = ctx.as_expression(block, &mut emitter);
2171 let resolution = proc::TypeResolution::Handle(result_ty);
2172 let converted =
2173 ectx.try_automatic_conversions(expr, &resolution, value_span)?;
2174 value = Some(converted);
2175 } else {
2176 value = Some(expr);
2177 }
2178 } else {
2179 value = None;
2180 }
2181 block.extend(emitter.finish(&ctx.function.expressions));
2182
2183 ir::Statement::Return { value }
2184 }
2185 ast::StatementKind::Kill => ir::Statement::Kill,
2186 ast::StatementKind::Call(ref call_phrase) => {
2187 let mut emitter = proc::Emitter::default();
2188 emitter.start(&ctx.function.expressions);
2189
2190 let _ = self.call(
2191 call_phrase,
2192 stmt.span,
2193 &mut ctx.as_expression(block, &mut emitter),
2194 true,
2195 )?;
2196 block.extend(emitter.finish(&ctx.function.expressions));
2197 return Ok(());
2198 }
2199 ast::StatementKind::Assign {
2200 target: ast_target,
2201 op,
2202 value,
2203 } => {
2204 let mut emitter = proc::Emitter::default();
2205 emitter.start(&ctx.function.expressions);
2206 let target_span = ctx.ast_expressions.get_span(ast_target);
2207
2208 let mut ectx = ctx.as_expression(block, &mut emitter);
2209 let target = self.expression_for_reference(ast_target, &mut ectx)?;
2210 let target_handle = match target {
2211 Typed::Reference(handle) => handle,
2212 Typed::Plain(handle) => {
2213 let ty = ctx.invalid_assignment_type(handle);
2214 return Err(Box::new(Error::InvalidAssignment {
2215 span: target_span,
2216 ty,
2217 }));
2218 }
2219 };
2220
2221 let target_scalar = match op {
2226 Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => {
2227 Some(ir::Scalar::U32)
2228 }
2229 _ => resolve_inner!(ectx, target_handle)
2230 .pointer_automatically_convertible_scalar(&ectx.module.types),
2231 };
2232
2233 let op_assign = if let Some(op) = op {
2235 Some((op, ectx.apply_load_rule(target)?))
2236 } else {
2237 None
2238 };
2239
2240 let value = self.expression_for_abstract(value, &mut ectx)?;
2241 let mut value = match target_scalar {
2242 Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar(
2243 value,
2244 target_scalar,
2245 target_span,
2246 )?,
2247 None => value,
2248 };
2249
2250 let value = match op_assign {
2251 Some((op, mut left)) => {
2252 ectx.binary_op_splat(op, &mut left, &mut value)?;
2253 ectx.append_expression(
2254 ir::Expression::Binary {
2255 op,
2256 left,
2257 right: value,
2258 },
2259 stmt.span,
2260 )?
2261 }
2262 None => value,
2263 };
2264 block.extend(emitter.finish(&ctx.function.expressions));
2265
2266 ir::Statement::Store {
2267 pointer: target_handle,
2268 value,
2269 }
2270 }
2271 ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {
2272 let mut emitter = proc::Emitter::default();
2273 emitter.start(&ctx.function.expressions);
2274
2275 let op = match stmt.kind {
2276 ast::StatementKind::Increment(_) => ir::BinaryOperator::Add,
2277 ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract,
2278 _ => unreachable!(),
2279 };
2280
2281 let value_span = ctx.ast_expressions.get_span(value);
2282 let target = self
2283 .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;
2284 let target_handle = target.ref_or(Error::BadIncrDecrReferenceType(value_span))?;
2285
2286 let mut ectx = ctx.as_expression(block, &mut emitter);
2287 let scalar = match *resolve_inner!(ectx, target_handle) {
2288 ir::TypeInner::ValuePointer {
2289 size: None, scalar, ..
2290 } => scalar,
2291 ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {
2292 ir::TypeInner::Scalar(scalar) => scalar,
2293 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2294 },
2295 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2296 };
2297 let literal = match scalar.kind {
2298 ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar)
2299 .ok_or(Error::BadIncrDecrReferenceType(value_span))?,
2300 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2301 };
2302
2303 let right =
2304 ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?;
2305 let rctx = ectx.runtime_expression_ctx(stmt.span)?;
2306 let left = rctx.function.expressions.append(
2307 ir::Expression::Load {
2308 pointer: target_handle,
2309 },
2310 value_span,
2311 );
2312 let value = rctx
2313 .function
2314 .expressions
2315 .append(ir::Expression::Binary { op, left, right }, stmt.span);
2316 rctx.local_expression_kind_tracker
2317 .insert(left, proc::ExpressionKind::Runtime);
2318 rctx.local_expression_kind_tracker
2319 .insert(value, proc::ExpressionKind::Runtime);
2320
2321 block.extend(emitter.finish(&ctx.function.expressions));
2322 ir::Statement::Store {
2323 pointer: target_handle,
2324 value,
2325 }
2326 }
2327 ast::StatementKind::ConstAssert(condition) => {
2328 let mut emitter = proc::Emitter::default();
2329 emitter.start(&ctx.function.expressions);
2330
2331 let condition =
2332 self.expression(condition, &mut ctx.as_const(block, &mut emitter))?;
2333
2334 let span = ctx.function.expressions.get_span(condition);
2335 match ctx
2336 .module
2337 .to_ctx()
2338 .get_const_val_from(condition, &ctx.function.expressions)
2339 {
2340 Ok(true) => Ok(()),
2341 Ok(false) => Err(Error::ConstAssertFailed(span)),
2342 Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
2343 unreachable!()
2344 }
2345 Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
2346 }?;
2347
2348 block.extend(emitter.finish(&ctx.function.expressions));
2349
2350 return Ok(());
2351 }
2352 ast::StatementKind::Phony(expr) => {
2353 let mut emitter = proc::Emitter::default();
2357 emitter.start(&ctx.function.expressions);
2358
2359 let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;
2360 block.extend(emitter.finish(&ctx.function.expressions));
2361 ctx.named_expressions
2362 .insert(value, ("phony".to_string(), stmt.span));
2363 return Ok(());
2364 }
2365 };
2366
2367 block.push(out, stmt.span);
2368
2369 Ok(())
2370 }
2371
2372 fn expression(
2380 &mut self,
2381 expr: Handle<ast::Expression<'source>>,
2382 ctx: &mut ExpressionContext<'source, '_, '_>,
2383 ) -> Result<'source, Handle<ir::Expression>> {
2384 let expr = self.expression_for_abstract(expr, ctx)?;
2385 ctx.concretize(expr)
2386 }
2387
2388 fn expression_for_abstract(
2389 &mut self,
2390 expr: Handle<ast::Expression<'source>>,
2391 ctx: &mut ExpressionContext<'source, '_, '_>,
2392 ) -> Result<'source, Handle<ir::Expression>> {
2393 let expr = self.expression_for_reference(expr, ctx)?;
2394 ctx.apply_load_rule(expr)
2395 }
2396
2397 fn expression_with_leaf_scalar(
2398 &mut self,
2399 expr: Handle<ast::Expression<'source>>,
2400 scalar: ir::Scalar,
2401 ctx: &mut ExpressionContext<'source, '_, '_>,
2402 ) -> Result<'source, Handle<ir::Expression>> {
2403 let unconverted = self.expression_for_abstract(expr, ctx)?;
2404 ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default())
2405 }
2406
2407 fn expression_for_reference(
2408 &mut self,
2409 expr: Handle<ast::Expression<'source>>,
2410 ctx: &mut ExpressionContext<'source, '_, '_>,
2411 ) -> Result<'source, Typed<Handle<ir::Expression>>> {
2412 let span = ctx.ast_expressions.get_span(expr);
2413 let expr = &ctx.ast_expressions[expr];
2414
2415 let expr: Typed<ir::Expression> = match *expr {
2416 ast::Expression::Literal(literal) => {
2417 let literal = match literal {
2418 ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f),
2419 ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f),
2420 ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i),
2421 ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u),
2422 ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i),
2423 ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u),
2424 ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f),
2425 ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i),
2426 ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f),
2427 ast::Literal::Bool(b) => ir::Literal::Bool(b),
2428 };
2429 let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?;
2430 return Ok(Typed::Plain(handle));
2431 }
2432 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2433 ref template_list, ..
2434 }) if !template_list.is_empty() => {
2435 return Err(Box::new(Error::UnexpectedTemplate(span)))
2436 }
2437 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2438 ident: ast::IdentExpr::Local(local),
2439 ..
2440 }) => {
2441 return ctx.local(&local, span);
2442 }
2443 ast::Expression::Ident(ast::TemplateElaboratedIdent {
2444 ident: ast::IdentExpr::Unresolved(name),
2445 ..
2446 }) => {
2447 let global = ctx
2448 .globals
2449 .get(name)
2450 .ok_or(Error::UnknownIdent(span, name))?;
2451 let expr = match *global {
2452 LoweredGlobalDecl::Var(handle) => {
2453 let expr = ir::Expression::GlobalVariable(handle);
2454 let v = &ctx.module.global_variables[handle];
2455 match v.space {
2456 ir::AddressSpace::Handle => Typed::Plain(expr),
2457 _ => Typed::Reference(expr),
2458 }
2459 }
2460 LoweredGlobalDecl::Const(handle) => {
2461 Typed::Plain(ir::Expression::Constant(handle))
2462 }
2463 LoweredGlobalDecl::Override(handle) => {
2464 Typed::Plain(ir::Expression::Override(handle))
2465 }
2466 LoweredGlobalDecl::Function { .. }
2467 | LoweredGlobalDecl::Type(_)
2468 | LoweredGlobalDecl::EntryPoint(_) => {
2469 return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable)));
2470 }
2471 };
2472
2473 return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));
2474 }
2475 ast::Expression::Unary { op, expr } => self.unary(op, expr, span, ctx)?,
2476 ast::Expression::AddrOf(expr) => {
2477 match self.expression_for_reference(expr, ctx)? {
2480 Typed::Reference(handle) => {
2481 let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle];
2482 if let &ir::Expression::Access { base, .. }
2483 | &ir::Expression::AccessIndex { base, .. } = expr
2484 {
2485 if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() {
2486 if matches!(
2487 *ty.inner_with(&ctx.module.types),
2488 ir::TypeInner::Vector { .. },
2489 ) {
2490 return Err(Box::new(Error::InvalidAddrOfOperand(
2491 ctx.get_expression_span(handle),
2492 )));
2493 }
2494 }
2495 }
2496 return Ok(Typed::Plain(handle));
2498 }
2499 Typed::Plain(_) => {
2500 return Err(Box::new(Error::NotReference(
2501 "the operand of the `&` operator",
2502 span,
2503 )));
2504 }
2505 }
2506 }
2507 ast::Expression::Deref(expr) => {
2508 let pointer = self.expression(expr, ctx)?;
2510
2511 if resolve_inner!(ctx, pointer).pointer_space().is_none() {
2512 return Err(Box::new(Error::NotPointer(span)));
2513 }
2514
2515 return Ok(Typed::Reference(pointer));
2517 }
2518 ast::Expression::Binary { op, left, right } => {
2519 self.binary(op, left, right, span, ctx)?
2520 }
2521 ast::Expression::Call(ref call_phrase) => {
2522 let handle = self
2523 .call(call_phrase, span, ctx, false)?
2524 .ok_or(Error::FunctionReturnsVoid(span))?;
2525 return Ok(Typed::Plain(handle));
2526 }
2527 ast::Expression::Index { base, index } => {
2528 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2529 let index = self.expression(index, ctx)?;
2530
2531 if let Typed::Plain(handle) = lowered_base {
2534 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2535 lowered_base = Typed::Reference(handle);
2536 }
2537 }
2538
2539 lowered_base.try_map(|base| match ctx.get_const_val(index).ok() {
2540 Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }),
2541 None => {
2542 let base = ctx.concretize(base)?;
2548 Ok(ir::Expression::Access { base, index })
2549 }
2550 })?
2551 }
2552 ast::Expression::Member { base, ref field } => {
2553 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2554
2555 if let Typed::Plain(handle) = lowered_base {
2558 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2559 lowered_base = Typed::Reference(handle);
2560 }
2561 }
2562
2563 let temp_ty;
2564 let composite_type: &ir::TypeInner = match lowered_base {
2565 Typed::Reference(handle) => {
2566 temp_ty = resolve_inner!(ctx, handle)
2567 .pointer_base_type()
2568 .expect("In Typed::Reference(handle), handle must be a Naga pointer");
2569 temp_ty.inner_with(&ctx.module.types)
2570 }
2571
2572 Typed::Plain(handle) => {
2573 resolve_inner!(ctx, handle)
2574 }
2575 };
2576
2577 let access = match *composite_type {
2578 ir::TypeInner::Struct { ref members, .. } => {
2579 let index = members
2580 .iter()
2581 .position(|m| m.name.as_deref() == Some(field.name))
2582 .ok_or(Error::BadAccessor(field.span))?
2583 as u32;
2584
2585 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2586 }
2587 ir::TypeInner::Vector { size: vec_size, .. } => {
2588 match Components::new(field.name, field.span)? {
2589 Components::Swizzle { size, pattern } => {
2590 for &component in pattern[..size as usize].iter() {
2591 if component as u8 >= vec_size as u8 {
2592 return Err(Box::new(Error::BadAccessor(field.span)));
2593 }
2594 }
2595 Typed::Plain(ir::Expression::Swizzle {
2596 size,
2597 vector: ctx.apply_load_rule(lowered_base)?,
2598 pattern,
2599 })
2600 }
2601 Components::Single(index) => {
2602 if index >= vec_size as u32 {
2603 return Err(Box::new(Error::BadAccessor(field.span)));
2604 }
2605 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2606 }
2607 }
2608 }
2609 _ => return Err(Box::new(Error::BadAccessor(field.span))),
2610 };
2611
2612 access
2613 }
2614 ast::Expression::String(_) => {
2615 return Err(Box::new(Error::InvalidStringLiteral {
2616 span,
2617 description: "String literals are only supported in debugPrintf",
2618 }))
2619 }
2620 };
2621
2622 expr.try_map(|handle| ctx.append_expression(handle, span))
2623 }
2624
2625 fn logical(
2629 &mut self,
2630 op: crate::BinaryOperator,
2631 left: Handle<crate::Expression>,
2632 right: Handle<ast::Expression<'source>>,
2633 span: Span,
2634 ctx: &mut ExpressionContext<'source, '_, '_>,
2635 ) -> Result<'source, Typed<crate::Expression>> {
2636 debug_assert!(
2637 op == crate::BinaryOperator::LogicalAnd || op == crate::BinaryOperator::LogicalOr
2638 );
2639
2640 if ctx.is_runtime() {
2641 let (condition, else_val) = if op == crate::BinaryOperator::LogicalAnd {
2653 let condition = left;
2654 let else_val = ctx.append_expression(
2655 crate::Expression::Literal(crate::Literal::Bool(false)),
2656 span,
2657 )?;
2658 (condition, else_val)
2659 } else {
2660 let condition = ctx.append_expression(
2661 crate::Expression::Unary {
2662 op: crate::UnaryOperator::LogicalNot,
2663 expr: left,
2664 },
2665 span,
2666 )?;
2667 let else_val = ctx.append_expression(
2668 crate::Expression::Literal(crate::Literal::Bool(true)),
2669 span,
2670 )?;
2671 (condition, else_val)
2672 };
2673
2674 let bool_ty = ctx.ensure_type_exists(crate::TypeInner::Scalar(crate::Scalar::BOOL));
2675
2676 let rctx = ctx.runtime_expression_ctx(span)?;
2677 let result_var = rctx.function.local_variables.append(
2678 crate::LocalVariable {
2679 name: None,
2680 ty: bool_ty,
2681 init: None,
2682 },
2683 span,
2684 );
2685 let pointer =
2686 ctx.append_expression(crate::Expression::LocalVariable(result_var), span)?;
2687
2688 let (right, mut accept) = ctx.with_nested_runtime_expression_ctx(span, |ctx| {
2689 let right = self.expression_for_abstract(right, ctx)?;
2690 ctx.grow_types(right)?;
2691 Ok(right)
2692 })?;
2693
2694 accept.push(
2695 crate::Statement::Store {
2696 pointer,
2697 value: right,
2698 },
2699 span,
2700 );
2701
2702 let mut reject = crate::Block::with_capacity(1);
2703 reject.push(
2704 crate::Statement::Store {
2705 pointer,
2706 value: else_val,
2707 },
2708 span,
2709 );
2710
2711 let rctx = ctx.runtime_expression_ctx(span)?;
2712 rctx.block.push(
2713 crate::Statement::If {
2714 condition,
2715 accept,
2716 reject,
2717 },
2718 span,
2719 );
2720
2721 Ok(Typed::Reference(crate::Expression::LocalVariable(
2722 result_var,
2723 )))
2724 } else {
2725 let left_val: Option<bool> = ctx.get_const_val(left).ok();
2726
2727 if left_val.is_some_and(|left_val| {
2728 op == crate::BinaryOperator::LogicalAnd && !left_val
2729 || op == crate::BinaryOperator::LogicalOr && left_val
2730 }) {
2731 Ok(Typed::Plain(ctx.get(left).clone()))
2740 } else {
2741 let right = self.expression_for_abstract(right, ctx)?;
2747 ctx.grow_types(right)?;
2748
2749 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2750 }
2751 }
2752 }
2753
2754 fn type_expression(
2755 &mut self,
2756 expr: Handle<ast::Expression<'source>>,
2757 ctx: &mut ExpressionContext<'source, '_, '_>,
2758 ) -> Result<'source, Handle<ir::Type>> {
2759 let span = ctx.ast_expressions.get_span(expr);
2760 let expr = &ctx.ast_expressions[expr];
2761
2762 let ident = match *expr {
2763 ast::Expression::Ident(ref ident) => ident,
2764 _ => return Err(Box::new(Error::UnexpectedExprForTypeExpression(span))),
2765 };
2766
2767 self.type_specifier(ident, ctx, None)
2768 }
2769
2770 fn type_specifier(
2771 &mut self,
2772 ident: &ast::TemplateElaboratedIdent<'source>,
2773 ctx: &mut ExpressionContext<'source, '_, '_>,
2774 alias_name: Option<String>,
2775 ) -> Result<'source, Handle<ir::Type>> {
2776 let &ast::TemplateElaboratedIdent {
2777 ref ident,
2778 ident_span,
2779 ref template_list,
2780 ..
2781 } = ident;
2782
2783 let ident = match *ident {
2784 ast::IdentExpr::Unresolved(ident) => ident,
2785 ast::IdentExpr::Local(_) => {
2786 return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2789 }
2790 };
2791
2792 let mut tl = TemplateListIter::new(ident_span, template_list);
2793
2794 if let Some(global) = ctx.globals.get(ident) {
2795 let &LoweredGlobalDecl::Type(handle) = global else {
2796 return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2797 };
2798
2799 tl.finish(ctx)?;
2803 return Ok(handle);
2804 }
2805
2806 let ty = conv::map_predeclared_type(&ctx.enable_extensions, ident_span, ident)?
2809 .ok_or_else(|| Box::new(Error::UnknownIdent(ident_span, ident)))?;
2810 let ty = self.finalize_type(ctx, ty, &mut tl, alias_name)?;
2811
2812 tl.finish(ctx)?;
2813
2814 Ok(ty)
2815 }
2816
2817 fn finalize_type(
2835 &mut self,
2836 ctx: &mut ExpressionContext<'source, '_, '_>,
2837 ty: conv::PredeclaredType,
2838 tl: &mut TemplateListIter<'_, 'source>,
2839 alias_name: Option<String>,
2840 ) -> Result<'source, Handle<ir::Type>> {
2841 let ty = match ty {
2842 conv::PredeclaredType::TypeInner(ty_inner) => {
2843 if let ir::TypeInner::Image {
2844 class: ir::ImageClass::External,
2845 ..
2846 } = ty_inner
2847 {
2848 ctx.module.generate_external_texture_types();
2863 }
2864
2865 ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2866 }
2867 conv::PredeclaredType::RayDesc => ctx.module.generate_ray_desc_type(),
2868 conv::PredeclaredType::RayIntersection => ctx.module.generate_ray_intersection_type(),
2869 conv::PredeclaredType::TypeGenerator(type_generator) => {
2870 let ty_inner = match type_generator {
2871 conv::TypeGenerator::Vector { size } => {
2872 let (scalar, _) = tl.scalar_ty(self, ctx)?;
2873 ir::TypeInner::Vector { size, scalar }
2874 }
2875 conv::TypeGenerator::Matrix { columns, rows } => {
2876 let (scalar, span) = tl.scalar_ty(self, ctx)?;
2877 if scalar.kind != ir::ScalarKind::Float {
2878 return Err(Box::new(Error::BadMatrixScalarKind(span, scalar)));
2879 }
2880 ir::TypeInner::Matrix {
2881 columns,
2882 rows,
2883 scalar,
2884 }
2885 }
2886 conv::TypeGenerator::Array => {
2887 let base = tl.ty(self, ctx)?;
2888 let size = tl.maybe_array_size(self, ctx)?;
2889
2890 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
2892 let LayoutErrorInner::TooLarge = err.inner else {
2893 unreachable!("unexpected layout error: {err:?}");
2894 };
2895 Box::new(Error::TypeTooLarge {
2898 span: ctx.module.types.get_span(err.ty),
2899 })
2900 })?;
2901 let stride = ctx.layouter[base].to_stride();
2902
2903 ir::TypeInner::Array { base, size, stride }
2904 }
2905 conv::TypeGenerator::Atomic => {
2906 let (scalar, _) = tl.scalar_ty(self, ctx)?;
2907 ir::TypeInner::Atomic(scalar)
2908 }
2909 conv::TypeGenerator::Pointer => {
2910 let mut space = tl.address_space(ctx)?;
2911 let base = tl.ty(self, ctx)?;
2912 tl.maybe_access_mode(&mut space, ctx)?;
2913 ir::TypeInner::Pointer { base, space }
2914 }
2915 conv::TypeGenerator::SampledTexture {
2916 dim,
2917 arrayed,
2918 multi,
2919 } => {
2920 let (scalar, span) = tl.scalar_ty(self, ctx)?;
2921 let ir::Scalar { kind, width } = scalar;
2922 if width != 4 {
2923 return Err(Box::new(Error::BadTextureSampleType { span, scalar }));
2924 }
2925 ir::TypeInner::Image {
2926 dim,
2927 arrayed,
2928 class: ir::ImageClass::Sampled { kind, multi },
2929 }
2930 }
2931 conv::TypeGenerator::StorageTexture { dim, arrayed } => {
2932 let format = tl.storage_format(ctx)?;
2933 let access = tl.access_mode(ctx)?;
2934 ir::TypeInner::Image {
2935 dim,
2936 arrayed,
2937 class: ir::ImageClass::Storage { format, access },
2938 }
2939 }
2940 conv::TypeGenerator::BindingArray => {
2941 let base = tl.ty(self, ctx)?;
2942 let size = tl.maybe_array_size(self, ctx)?;
2943 ir::TypeInner::BindingArray { base, size }
2944 }
2945 conv::TypeGenerator::AccelerationStructure => {
2946 let vertex_return = tl.maybe_vertex_return(ctx)?;
2947 ir::TypeInner::AccelerationStructure { vertex_return }
2948 }
2949 conv::TypeGenerator::RayQuery => {
2950 let vertex_return = tl.maybe_vertex_return(ctx)?;
2951 ir::TypeInner::RayQuery { vertex_return }
2952 }
2953 conv::TypeGenerator::CooperativeMatrix { columns, rows } => {
2954 let (ty, span) = tl.ty_with_span(self, ctx)?;
2955 let ir::TypeInner::Scalar(scalar) = ctx.module.types[ty].inner else {
2956 return Err(Box::new(Error::UnsupportedCooperativeScalar(span)));
2957 };
2958 let role = tl.cooperative_role(ctx)?;
2959 ir::TypeInner::CooperativeMatrix {
2960 columns,
2961 rows,
2962 scalar,
2963 role,
2964 }
2965 }
2966 };
2967 ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2968 }
2969 };
2970 Ok(ty)
2971 }
2972
2973 fn unary(
2974 &mut self,
2975 op: ir::UnaryOperator,
2976 expr: Handle<ast::Expression<'source>>,
2977 span: Span,
2978 ctx: &mut ExpressionContext<'source, '_, '_>,
2979 ) -> Result<'source, Typed<ir::Expression>> {
2980 let make_error = |operand_type: String| Error::InvalidUnaryOperandType {
2981 span,
2982 op,
2983 operand_type,
2984 };
2985
2986 let expr = self.expression_for_abstract(expr, ctx)?;
2987 ctx.grow_types(expr)?;
2988 let expr_ty_resolution = resolve!(ctx, expr);
2989
2990 let Some(kind) = expr_ty_resolution
2992 .inner_with(&ctx.module.types)
2993 .vector_size_and_scalar()
2994 .map(|(_, scalar)| scalar.kind)
2995 else {
2996 let operand_type = ctx.type_resolution_to_string(expr_ty_resolution);
2997 return Err(Box::new(make_error(operand_type)));
2998 };
2999 match (op, kind) {
3001 (ir::UnaryOperator::LogicalNot, ir::ScalarKind::Bool) => {}
3003
3004 (
3007 ir::UnaryOperator::Negate,
3008 ir::ScalarKind::AbstractInt
3009 | ir::ScalarKind::AbstractFloat
3010 | ir::ScalarKind::Sint
3011 | ir::ScalarKind::Float,
3012 ) => {}
3013
3014 (
3017 ir::UnaryOperator::BitwiseNot,
3018 ir::ScalarKind::Sint | ir::ScalarKind::Uint | ir::ScalarKind::AbstractInt,
3019 ) => {}
3020
3021 _ => {
3022 let operand_type = ctx.type_resolution_to_string(expr_ty_resolution);
3023 return Err(Box::new(make_error(operand_type)));
3024 }
3025 }
3026
3027 Ok(Typed::Plain(ir::Expression::Unary { op, expr }))
3028 }
3029
3030 fn binary(
3031 &mut self,
3032 op: ir::BinaryOperator,
3033 left: Handle<ast::Expression<'source>>,
3034 right: Handle<ast::Expression<'source>>,
3035 span: Span,
3036 ctx: &mut ExpressionContext<'source, '_, '_>,
3037 ) -> Result<'source, Typed<ir::Expression>> {
3038 if op == ir::BinaryOperator::LogicalAnd || op == ir::BinaryOperator::LogicalOr {
3039 let left = self.expression_for_abstract(left, ctx)?;
3040 ctx.grow_types(left)?;
3041
3042 if !matches!(
3043 resolve_inner!(ctx, left),
3044 &ir::TypeInner::Scalar(ir::Scalar::BOOL)
3045 ) {
3046 let right = self.expression_for_abstract(right, ctx)?;
3048 ctx.grow_types(right)?;
3049 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
3050 } else {
3051 self.logical(op, left, right, span, ctx)
3052 }
3053 } else {
3054 let mut left = self.expression_for_abstract(left, ctx)?;
3056 let mut right = self.expression_for_abstract(right, ctx)?;
3057
3058 ctx.binary_op_splat(op, &mut left, &mut right)?;
3061
3062 match op {
3064 ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => {
3065 right =
3070 ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?;
3071
3072 if !ctx.is_const(right) {
3084 left = ctx.concretize(left)?;
3085 }
3086 }
3087
3088 _ => {
3093 ctx.grow_types(left)?;
3094 ctx.grow_types(right)?;
3095 if let Ok(consensus_scalar) =
3096 ctx.automatic_conversion_consensus(None, [left, right].iter())
3097 {
3098 ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;
3099 ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;
3100 }
3101 }
3102 }
3103
3104 Ok(Typed::Plain(ir::Expression::Binary { op, left, right }))
3105 }
3106 }
3107
3108 #[allow(clippy::too_many_arguments)]
3110 fn call_builtin<'phrase>(
3111 &mut self,
3112 function_name: &'source str,
3113 function_span: Span,
3114 arguments: &[Handle<ast::Expression<'source>>],
3115 template_params: &mut TemplateListIter<'phrase, 'source>,
3116 call_span: Span,
3117 ctx: &mut ExpressionContext<'source, '_, '_>,
3118 is_statement: bool,
3119 ) -> Result<'source, Option<(Handle<ir::Expression>, MustUse)>> {
3120 let (expr, must_use) = if let Some(fun) = conv::map_relational_fun(function_name) {
3121 let mut args = ctx.prepare_args(arguments, 1, function_span);
3122 let argument = self.expression(args.next()?, ctx)?;
3123 args.finish()?;
3124
3125 let argument_unmodified = matches!(
3127 fun,
3128 ir::RelationalFunction::All | ir::RelationalFunction::Any
3129 ) && {
3130 matches!(
3131 resolve_inner!(ctx, argument),
3132 &ir::TypeInner::Scalar(ir::Scalar {
3133 kind: ir::ScalarKind::Bool,
3134 ..
3135 })
3136 )
3137 };
3138
3139 if argument_unmodified {
3140 return Ok(Some((argument, MustUse::Yes)));
3141 } else {
3142 (ir::Expression::Relational { fun, argument }, MustUse::Yes)
3143 }
3144 } else if let Some((axis, ctrl)) = conv::map_derivative(function_name) {
3145 let mut args = ctx.prepare_args(arguments, 1, function_span);
3146 let expr = self.expression(args.next()?, ctx)?;
3147 args.finish()?;
3148
3149 (
3150 ir::Expression::Derivative { axis, ctrl, expr },
3151 MustUse::Yes,
3152 )
3153 } else if let Some(fun) = conv::map_standard_fun(function_name) {
3154 (
3155 self.math_function_helper(function_span, fun, arguments, ctx)?,
3156 MustUse::Yes,
3157 )
3158 } else if let Some(fun) = Texture::map(function_name) {
3159 (
3160 self.texture_sample_helper(fun, arguments, function_span, ctx)?,
3161 MustUse::Yes,
3162 )
3163 } else if let Some((op, cop)) = conv::map_subgroup_operation(function_name) {
3164 return Ok(Some((
3165 self.subgroup_operation_helper(function_span, op, cop, arguments, ctx)?,
3166 MustUse::Yes,
3167 )));
3168 } else if let Some(mode) = SubgroupGather::map(function_name) {
3169 return Ok(Some((
3170 self.subgroup_gather_helper(function_span, mode, arguments, ctx)?,
3171 MustUse::Yes,
3172 )));
3173 } else if let Some(fun) = ir::AtomicFunction::map(function_name) {
3174 return Ok(self
3175 .atomic_helper(function_span, fun, arguments, is_statement, ctx)?
3176 .map(|result| (result, MustUse::No)));
3177 } else {
3178 match function_name {
3179 "bitcast" => {
3180 let ty = template_params.ty(self, ctx)?;
3181
3182 let mut args = ctx.prepare_args(arguments, 1, function_span);
3183 let expr = self.expression(args.next()?, ctx)?;
3184 args.finish()?;
3185
3186 let element_scalar = match ctx.module.types[ty].inner {
3187 ir::TypeInner::Scalar(scalar) => scalar,
3188 ir::TypeInner::Vector { scalar, .. } => scalar,
3189 _ => {
3190 let ty_resolution = resolve!(ctx, expr);
3191 return Err(Box::new(Error::BadTypeCast {
3192 from_type: ctx.type_resolution_to_string(ty_resolution),
3193 span: function_span,
3194 to_type: ctx.type_to_string(ty),
3195 }));
3196 }
3197 };
3198
3199 (
3200 ir::Expression::As {
3201 expr,
3202 kind: element_scalar.kind,
3203 convert: None,
3204 },
3205 MustUse::Yes,
3206 )
3207 }
3208 "coopLoad" | "coopLoadT" => {
3209 let row_major = function_name.ends_with("T");
3210 let (matrix_ty, matrix_span) = template_params.ty_with_span(self, ctx)?;
3211
3212 let mut args = ctx.prepare_args(arguments, 1, call_span);
3213 let pointer = self.expression(args.next()?, ctx)?;
3214 let (columns, rows, role) = match ctx.module.types[matrix_ty].inner {
3215 ir::TypeInner::CooperativeMatrix {
3216 columns,
3217 rows,
3218 role,
3219 ..
3220 } => (columns, rows, role),
3221 _ => return Err(Box::new(Error::InvalidCooperativeLoadType(matrix_span))),
3222 };
3223 let stride = if args.total_args > 1 {
3224 self.expression(args.next()?, ctx)?
3225 } else {
3226 let stride = if row_major {
3228 columns as u32
3229 } else {
3230 rows as u32
3231 };
3232 ctx.append_expression(
3233 ir::Expression::Literal(ir::Literal::U32(stride)),
3234 Span::UNDEFINED,
3235 )?
3236 };
3237 args.finish()?;
3238
3239 (
3240 crate::Expression::CooperativeLoad {
3241 columns,
3242 rows,
3243 role,
3244 data: crate::CooperativeData {
3245 pointer,
3246 stride,
3247 row_major,
3248 },
3249 },
3250 MustUse::Yes,
3251 )
3252 }
3253 "select" => {
3254 let mut args = ctx.prepare_args(arguments, 3, function_span);
3255
3256 let reject_orig = args.next()?;
3257 let accept_orig = args.next()?;
3258 let mut values = [
3259 self.expression_for_abstract(reject_orig, ctx)?,
3260 self.expression_for_abstract(accept_orig, ctx)?,
3261 ];
3262 let condition = self.expression(args.next()?, ctx)?;
3263
3264 args.finish()?;
3265
3266 let diagnostic_details =
3267 |ctx: &ExpressionContext<'_, '_, '_>,
3268 ty_res: &proc::TypeResolution,
3269 orig_expr| {
3270 (
3271 ctx.ast_expressions.get_span(orig_expr),
3272 format!("`{}`", ctx.as_diagnostic_display(ty_res)),
3273 )
3274 };
3275 for (&value, orig_value) in values.iter().zip([reject_orig, accept_orig]) {
3276 let value_ty_res = resolve!(ctx, value);
3277 if value_ty_res
3278 .inner_with(&ctx.module.types)
3279 .vector_size_and_scalar()
3280 .is_none()
3281 {
3282 let (arg_span, arg_type) =
3283 diagnostic_details(ctx, value_ty_res, orig_value);
3284 return Err(Box::new(Error::SelectUnexpectedArgumentType {
3285 arg_span,
3286 arg_type,
3287 }));
3288 }
3289 }
3290 let mut consensus_scalar = ctx
3291 .automatic_conversion_consensus(None, &values)
3292 .map_err(|_idx| {
3293 let [reject, accept] = values;
3294 let [(reject_span, reject_type), (accept_span, accept_type)] =
3295 [(reject_orig, reject), (accept_orig, accept)].map(
3296 |(orig_expr, expr)| {
3297 let ty_res = &ctx.typifier()[expr];
3298 diagnostic_details(ctx, ty_res, orig_expr)
3299 },
3300 );
3301 Error::SelectRejectAndAcceptHaveNoCommonType {
3302 reject_span,
3303 reject_type,
3304 accept_span,
3305 accept_type,
3306 }
3307 })?;
3308 if !ctx.is_const(condition) {
3309 consensus_scalar = consensus_scalar.concretize();
3310 }
3311
3312 ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;
3313
3314 let [reject, accept] = values;
3315
3316 (
3317 ir::Expression::Select {
3318 reject,
3319 accept,
3320 condition,
3321 },
3322 MustUse::Yes,
3323 )
3324 }
3325 "arrayLength" => {
3326 let mut args = ctx.prepare_args(arguments, 1, function_span);
3327 let expr = self.expression(args.next()?, ctx)?;
3328 args.finish()?;
3329
3330 (ir::Expression::ArrayLength(expr), MustUse::Yes)
3331 }
3332 "atomicLoad" => {
3333 let mut args = ctx.prepare_args(arguments, 1, function_span);
3334 let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?;
3335 args.finish()?;
3336
3337 (ir::Expression::Load { pointer }, MustUse::No)
3338 }
3339 "atomicStore" => {
3340 let mut args = ctx.prepare_args(arguments, 2, function_span);
3341 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3342 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3343 args.finish()?;
3344
3345 let rctx = ctx.runtime_expression_ctx(function_span)?;
3346 rctx.block
3347 .extend(rctx.emitter.finish(&rctx.function.expressions));
3348 rctx.emitter.start(&rctx.function.expressions);
3349 rctx.block
3350 .push(ir::Statement::Store { pointer, value }, function_span);
3351 return Ok(None);
3352 }
3353 "atomicCompareExchangeWeak" => {
3354 let mut args = ctx.prepare_args(arguments, 3, function_span);
3355
3356 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3357
3358 let compare = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3359
3360 let value = args.next()?;
3361 let value_span = ctx.ast_expressions.get_span(value);
3362 let value = self.expression_with_leaf_scalar(value, scalar, ctx)?;
3363
3364 args.finish()?;
3365
3366 let expression = match *resolve_inner!(ctx, value) {
3367 ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult {
3368 ty: ctx.module.generate_predeclared_type(
3369 ir::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
3370 ),
3371 comparison: true,
3372 },
3373 _ => return Err(Box::new(Error::InvalidAtomicOperandType(value_span))),
3374 };
3375
3376 let result = ctx.interrupt_emitter(expression, function_span)?;
3377 let rctx = ctx.runtime_expression_ctx(function_span)?;
3378 rctx.block.push(
3379 ir::Statement::Atomic {
3380 pointer,
3381 fun: ir::AtomicFunction::Exchange {
3382 compare: Some(compare),
3383 },
3384 value,
3385 result: Some(result),
3386 },
3387 function_span,
3388 );
3389 return Ok(Some((result, MustUse::No)));
3390 }
3391 "textureAtomicMin" | "textureAtomicMax" | "textureAtomicAdd"
3392 | "textureAtomicAnd" | "textureAtomicOr" | "textureAtomicXor" => {
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 (_, 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
3409 let value = self.expression(args.next()?, ctx)?;
3410
3411 args.finish()?;
3412
3413 let rctx = ctx.runtime_expression_ctx(function_span)?;
3414 rctx.block
3415 .extend(rctx.emitter.finish(&rctx.function.expressions));
3416 rctx.emitter.start(&rctx.function.expressions);
3417 let stmt = ir::Statement::ImageAtomic {
3418 image,
3419 coordinate,
3420 array_index,
3421 fun: match function_name {
3422 "textureAtomicMin" => ir::AtomicFunction::Min,
3423 "textureAtomicMax" => ir::AtomicFunction::Max,
3424 "textureAtomicAdd" => ir::AtomicFunction::Add,
3425 "textureAtomicAnd" => ir::AtomicFunction::And,
3426 "textureAtomicOr" => ir::AtomicFunction::InclusiveOr,
3427 "textureAtomicXor" => ir::AtomicFunction::ExclusiveOr,
3428 _ => unreachable!(),
3429 },
3430 value,
3431 };
3432 rctx.block.push(stmt, function_span);
3433 return Ok(None);
3434 }
3435 "storageBarrier" => {
3436 ctx.prepare_args(arguments, 0, function_span).finish()?;
3437
3438 let rctx = ctx.runtime_expression_ctx(function_span)?;
3439 rctx.block.push(
3440 ir::Statement::ControlBarrier(ir::Barrier::STORAGE),
3441 function_span,
3442 );
3443 return Ok(None);
3444 }
3445 "workgroupBarrier" => {
3446 ctx.prepare_args(arguments, 0, function_span).finish()?;
3447
3448 let rctx = ctx.runtime_expression_ctx(function_span)?;
3449 rctx.block.push(
3450 ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP),
3451 function_span,
3452 );
3453 return Ok(None);
3454 }
3455 "subgroupBarrier" => {
3456 ctx.prepare_args(arguments, 0, function_span).finish()?;
3457
3458 let rctx = ctx.runtime_expression_ctx(function_span)?;
3459 rctx.block.push(
3460 ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP),
3461 function_span,
3462 );
3463 return Ok(None);
3464 }
3465 "textureBarrier" => {
3466 ctx.prepare_args(arguments, 0, function_span).finish()?;
3467
3468 let rctx = ctx.runtime_expression_ctx(function_span)?;
3469 rctx.block.push(
3470 ir::Statement::ControlBarrier(ir::Barrier::TEXTURE),
3471 function_span,
3472 );
3473 return Ok(None);
3474 }
3475 "workgroupUniformLoad" => {
3476 let mut args = ctx.prepare_args(arguments, 1, function_span);
3477 let expr = args.next()?;
3478 args.finish()?;
3479
3480 let pointer = self.expression(expr, ctx)?;
3481 let result_ty = match *resolve_inner!(ctx, pointer) {
3482 ir::TypeInner::Pointer {
3483 base,
3484 space: ir::AddressSpace::WorkGroup,
3485 } => match ctx.module.types[base].inner {
3486 ir::TypeInner::Atomic(scalar) => ctx.module.types.insert(
3489 ir::Type {
3490 name: None,
3491 inner: ir::TypeInner::Scalar(scalar),
3492 },
3493 function_span,
3494 ),
3495 _ => base,
3496 },
3497 ir::TypeInner::ValuePointer {
3498 size,
3499 scalar,
3500 space: ir::AddressSpace::WorkGroup,
3501 } => ctx.module.types.insert(
3502 ir::Type {
3503 name: None,
3504 inner: match size {
3505 Some(size) => ir::TypeInner::Vector { size, scalar },
3506 None => ir::TypeInner::Scalar(scalar),
3507 },
3508 },
3509 function_span,
3510 ),
3511 _ => {
3512 let span = ctx.ast_expressions.get_span(expr);
3513 return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span)));
3514 }
3515 };
3516 let result = ctx.interrupt_emitter(
3517 ir::Expression::WorkGroupUniformLoadResult { ty: result_ty },
3518 function_span,
3519 )?;
3520 let rctx = ctx.runtime_expression_ctx(function_span)?;
3521 rctx.block.push(
3522 ir::Statement::WorkGroupUniformLoad { pointer, result },
3523 function_span,
3524 );
3525
3526 return Ok(Some((result, MustUse::Yes)));
3527 }
3528 "textureStore" => {
3529 let mut args = ctx.prepare_args(arguments, 3, function_span);
3530
3531 let image = args.next()?;
3532 let image_span = ctx.ast_expressions.get_span(image);
3533 let image = self.expression(image, ctx)?;
3534
3535 let coordinate = self.expression(args.next()?, ctx)?;
3536
3537 let (class, arrayed) = ctx.image_data(image, image_span)?;
3538 let array_index = arrayed
3539 .then(|| {
3540 args.min_args += 1;
3541 self.expression(args.next()?, ctx)
3542 })
3543 .transpose()?;
3544 let scalar = if let ir::ImageClass::Storage { format, .. } = class {
3545 format.into()
3546 } else {
3547 return Err(Box::new(Error::NotStorageTexture(image_span)));
3548 };
3549
3550 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3551
3552 args.finish()?;
3553
3554 let rctx = ctx.runtime_expression_ctx(function_span)?;
3555 rctx.block
3556 .extend(rctx.emitter.finish(&rctx.function.expressions));
3557 rctx.emitter.start(&rctx.function.expressions);
3558 let stmt = ir::Statement::ImageStore {
3559 image,
3560 coordinate,
3561 array_index,
3562 value,
3563 };
3564 rctx.block.push(stmt, function_span);
3565 return Ok(None);
3566 }
3567 "textureLoad" => {
3568 let mut args = ctx.prepare_args(arguments, 2, function_span);
3569
3570 let image = args.next()?;
3571 let image_span = ctx.ast_expressions.get_span(image);
3572 let image = self.expression(image, ctx)?;
3573
3574 let coordinate = self.expression(args.next()?, ctx)?;
3575
3576 let (class, arrayed) = ctx.image_data(image, image_span)?;
3577 let array_index = arrayed
3578 .then(|| {
3579 args.min_args += 1;
3580 self.expression(args.next()?, ctx)
3581 })
3582 .transpose()?;
3583
3584 let level = class
3585 .is_mipmapped()
3586 .then(|| {
3587 args.min_args += 1;
3588 self.expression(args.next()?, ctx)
3589 })
3590 .transpose()?;
3591
3592 let sample = class
3593 .is_multisampled()
3594 .then(|| self.expression(args.next()?, ctx))
3595 .transpose()?;
3596
3597 args.finish()?;
3598
3599 (
3600 ir::Expression::ImageLoad {
3601 image,
3602 coordinate,
3603 array_index,
3604 level,
3605 sample,
3606 },
3607 MustUse::Yes,
3608 )
3609 }
3610 "textureDimensions" => {
3611 let mut args = ctx.prepare_args(arguments, 1, function_span);
3612 let image = self.expression(args.next()?, ctx)?;
3613 let level = args
3614 .next()
3615 .map(|arg| self.expression(arg, ctx))
3616 .ok()
3617 .transpose()?;
3618 args.finish()?;
3619
3620 (
3621 ir::Expression::ImageQuery {
3622 image,
3623 query: ir::ImageQuery::Size { level },
3624 },
3625 MustUse::Yes,
3626 )
3627 }
3628 "textureNumLevels" => {
3629 let mut args = ctx.prepare_args(arguments, 1, function_span);
3630 let image = self.expression(args.next()?, ctx)?;
3631 args.finish()?;
3632
3633 (
3634 ir::Expression::ImageQuery {
3635 image,
3636 query: ir::ImageQuery::NumLevels,
3637 },
3638 MustUse::Yes,
3639 )
3640 }
3641 "textureNumLayers" => {
3642 let mut args = ctx.prepare_args(arguments, 1, function_span);
3643 let image = self.expression(args.next()?, ctx)?;
3644 args.finish()?;
3645
3646 (
3647 ir::Expression::ImageQuery {
3648 image,
3649 query: ir::ImageQuery::NumLayers,
3650 },
3651 MustUse::Yes,
3652 )
3653 }
3654 "textureNumSamples" => {
3655 let mut args = ctx.prepare_args(arguments, 1, function_span);
3656 let image = self.expression(args.next()?, ctx)?;
3657 args.finish()?;
3658
3659 (
3660 ir::Expression::ImageQuery {
3661 image,
3662 query: ir::ImageQuery::NumSamples,
3663 },
3664 MustUse::Yes,
3665 )
3666 }
3667 "rayQueryInitialize" => {
3668 let mut args = ctx.prepare_args(arguments, 3, function_span);
3669 let query = self.ray_query_pointer(args.next()?, ctx)?;
3670 let acceleration_structure = self.expression(args.next()?, ctx)?;
3671 let descriptor = self.expression(args.next()?, ctx)?;
3672 args.finish()?;
3673
3674 let _ = ctx.module.generate_ray_desc_type();
3675 let fun = ir::RayQueryFunction::Initialize {
3676 acceleration_structure,
3677 descriptor,
3678 };
3679
3680 let rctx = ctx.runtime_expression_ctx(function_span)?;
3681 rctx.block
3682 .extend(rctx.emitter.finish(&rctx.function.expressions));
3683 rctx.emitter.start(&rctx.function.expressions);
3684 rctx.block
3685 .push(ir::Statement::RayQuery { query, fun }, function_span);
3686 return Ok(None);
3687 }
3688 "getCommittedHitVertexPositions" => {
3689 let mut args = ctx.prepare_args(arguments, 1, function_span);
3690 let query = self.ray_query_pointer(args.next()?, ctx)?;
3691 args.finish()?;
3692
3693 let _ = ctx.module.generate_vertex_return_type();
3694
3695 (
3696 ir::Expression::RayQueryVertexPositions {
3697 query,
3698 committed: true,
3699 },
3700 MustUse::No,
3701 )
3702 }
3703 "getCandidateHitVertexPositions" => {
3704 let mut args = ctx.prepare_args(arguments, 1, function_span);
3705 let query = self.ray_query_pointer(args.next()?, ctx)?;
3706 args.finish()?;
3707
3708 let _ = ctx.module.generate_vertex_return_type();
3709
3710 (
3711 ir::Expression::RayQueryVertexPositions {
3712 query,
3713 committed: false,
3714 },
3715 MustUse::No,
3716 )
3717 }
3718 "rayQueryProceed" => {
3719 let mut args = ctx.prepare_args(arguments, 1, function_span);
3720 let query = self.ray_query_pointer(args.next()?, ctx)?;
3721 args.finish()?;
3722
3723 let result = ctx
3724 .interrupt_emitter(ir::Expression::RayQueryProceedResult, function_span)?;
3725 let fun = ir::RayQueryFunction::Proceed { result };
3726 let rctx = ctx.runtime_expression_ctx(function_span)?;
3727 rctx.block
3728 .push(ir::Statement::RayQuery { query, fun }, function_span);
3729 return Ok(Some((result, MustUse::No)));
3730 }
3731 "rayQueryGenerateIntersection" => {
3732 let mut args = ctx.prepare_args(arguments, 2, function_span);
3733 let query = self.ray_query_pointer(args.next()?, ctx)?;
3734 let hit_t = self.expression(args.next()?, ctx)?;
3735 args.finish()?;
3736
3737 let fun = ir::RayQueryFunction::GenerateIntersection { hit_t };
3738 let rctx = ctx.runtime_expression_ctx(function_span)?;
3739 rctx.block
3740 .push(ir::Statement::RayQuery { query, fun }, function_span);
3741 return Ok(None);
3742 }
3743 "rayQueryConfirmIntersection" => {
3744 let mut args = ctx.prepare_args(arguments, 1, function_span);
3745 let query = self.ray_query_pointer(args.next()?, ctx)?;
3746 args.finish()?;
3747
3748 let fun = ir::RayQueryFunction::ConfirmIntersection;
3749 let rctx = ctx.runtime_expression_ctx(function_span)?;
3750 rctx.block
3751 .push(ir::Statement::RayQuery { query, fun }, function_span);
3752 return Ok(None);
3753 }
3754 "rayQueryTerminate" => {
3755 let mut args = ctx.prepare_args(arguments, 1, function_span);
3756 let query = self.ray_query_pointer(args.next()?, ctx)?;
3757 args.finish()?;
3758
3759 let fun = ir::RayQueryFunction::Terminate;
3760 let rctx = ctx.runtime_expression_ctx(function_span)?;
3761 rctx.block
3762 .push(ir::Statement::RayQuery { query, fun }, function_span);
3763 return Ok(None);
3764 }
3765 "rayQueryGetCommittedIntersection" => {
3766 let mut args = ctx.prepare_args(arguments, 1, function_span);
3767 let query = self.ray_query_pointer(args.next()?, ctx)?;
3768 args.finish()?;
3769
3770 let _ = ctx.module.generate_ray_intersection_type();
3771 (
3772 ir::Expression::RayQueryGetIntersection {
3773 query,
3774 committed: true,
3775 },
3776 MustUse::No,
3777 )
3778 }
3779 "rayQueryGetCandidateIntersection" => {
3780 let mut args = ctx.prepare_args(arguments, 1, function_span);
3781 let query = self.ray_query_pointer(args.next()?, ctx)?;
3782 args.finish()?;
3783
3784 let _ = ctx.module.generate_ray_intersection_type();
3785 (
3786 ir::Expression::RayQueryGetIntersection {
3787 query,
3788 committed: false,
3789 },
3790 MustUse::No,
3791 )
3792 }
3793 "subgroupBallot" => {
3794 let mut args = ctx.prepare_args(arguments, 0, function_span);
3795 let predicate = if arguments.len() == 1 {
3796 Some(self.expression(args.next()?, ctx)?)
3797 } else {
3798 None
3799 };
3800 args.finish()?;
3801
3802 let result =
3803 ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, function_span)?;
3804 let rctx = ctx.runtime_expression_ctx(function_span)?;
3805 rctx.block.push(
3806 ir::Statement::SubgroupBallot { result, predicate },
3807 function_span,
3808 );
3809 return Ok(Some((result, MustUse::Yes)));
3810 }
3811 "quadSwapX" => {
3812 let mut args = ctx.prepare_args(arguments, 1, function_span);
3813
3814 let argument = self.expression(args.next()?, ctx)?;
3815 args.finish()?;
3816
3817 let ty = ctx.register_type(argument)?;
3818
3819 let result = ctx.interrupt_emitter(
3820 crate::Expression::SubgroupOperationResult { ty },
3821 function_span,
3822 )?;
3823 let rctx = ctx.runtime_expression_ctx(function_span)?;
3824 rctx.block.push(
3825 crate::Statement::SubgroupGather {
3826 mode: crate::GatherMode::QuadSwap(crate::Direction::X),
3827 argument,
3828 result,
3829 },
3830 function_span,
3831 );
3832 return Ok(Some((result, MustUse::Yes)));
3833 }
3834 "quadSwapY" => {
3835 let mut args = ctx.prepare_args(arguments, 1, function_span);
3836
3837 let argument = self.expression(args.next()?, ctx)?;
3838 args.finish()?;
3839
3840 let ty = ctx.register_type(argument)?;
3841
3842 let result = ctx.interrupt_emitter(
3843 crate::Expression::SubgroupOperationResult { ty },
3844 function_span,
3845 )?;
3846 let rctx = ctx.runtime_expression_ctx(function_span)?;
3847 rctx.block.push(
3848 crate::Statement::SubgroupGather {
3849 mode: crate::GatherMode::QuadSwap(crate::Direction::Y),
3850 argument,
3851 result,
3852 },
3853 function_span,
3854 );
3855 return Ok(Some((result, MustUse::Yes)));
3856 }
3857 "quadSwapDiagonal" => {
3858 let mut args = ctx.prepare_args(arguments, 1, function_span);
3859
3860 let argument = self.expression(args.next()?, ctx)?;
3861 args.finish()?;
3862
3863 let ty = ctx.register_type(argument)?;
3864
3865 let result = ctx.interrupt_emitter(
3866 crate::Expression::SubgroupOperationResult { ty },
3867 function_span,
3868 )?;
3869 let rctx = ctx.runtime_expression_ctx(function_span)?;
3870 rctx.block.push(
3871 crate::Statement::SubgroupGather {
3872 mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal),
3873 argument,
3874 result,
3875 },
3876 function_span,
3877 );
3878 return Ok(Some((result, MustUse::Yes)));
3879 }
3880 "coopStore" | "coopStoreT" => {
3881 let row_major = function_name.ends_with("T");
3882
3883 let mut args = ctx.prepare_args(arguments, 2, function_span);
3884 let target = self.expression(args.next()?, ctx)?;
3885 let pointer = self.expression(args.next()?, ctx)?;
3886 let stride = if args.total_args > 2 {
3887 self.expression(args.next()?, ctx)?
3888 } else {
3889 let stride = match *resolve_inner!(ctx, target) {
3891 ir::TypeInner::CooperativeMatrix { columns, rows, .. } => {
3892 if row_major {
3893 columns as u32
3894 } else {
3895 rows as u32
3896 }
3897 }
3898 _ => 0,
3899 };
3900 ctx.append_expression(
3901 ir::Expression::Literal(ir::Literal::U32(stride)),
3902 Span::UNDEFINED,
3903 )?
3904 };
3905 args.finish()?;
3906
3907 let rctx = ctx.runtime_expression_ctx(function_span)?;
3908 rctx.block.push(
3909 crate::Statement::CooperativeStore {
3910 target,
3911 data: crate::CooperativeData {
3912 pointer,
3913 stride,
3914 row_major,
3915 },
3916 },
3917 function_span,
3918 );
3919 return Ok(None);
3920 }
3921 "coopMultiplyAdd" => {
3922 let mut args = ctx.prepare_args(arguments, 3, function_span);
3923 let a = self.expression(args.next()?, ctx)?;
3924 let b = self.expression(args.next()?, ctx)?;
3925 let c = self.expression(args.next()?, ctx)?;
3926 args.finish()?;
3927
3928 (
3929 ir::Expression::CooperativeMultiplyAdd { a, b, c },
3930 MustUse::Yes,
3931 )
3932 }
3933 "traceRay" => {
3934 let mut args = ctx.prepare_args(arguments, 3, function_span);
3935 let acceleration_structure = self.expression(args.next()?, ctx)?;
3936 let descriptor = self.expression(args.next()?, ctx)?;
3937 let payload = self.expression(args.next()?, ctx)?;
3938 args.finish()?;
3939
3940 let _ = ctx.module.generate_ray_desc_type();
3941 let fun = ir::RayPipelineFunction::TraceRay {
3942 acceleration_structure,
3943 descriptor,
3944 payload,
3945 };
3946
3947 let rctx = ctx.runtime_expression_ctx(function_span)?;
3948 rctx.block
3949 .extend(rctx.emitter.finish(&rctx.function.expressions));
3950 rctx.emitter.start(&rctx.function.expressions);
3951 rctx.block
3952 .push(ir::Statement::RayPipelineFunction(fun), function_span);
3953 return Ok(None);
3954 }
3955 "debugPrintf" => {
3956 if !ctx
3957 .enable_extensions
3958 .contains(crate::front::wgsl::ImplementedEnableExtension::WgpuDebugPrintf)
3959 {
3960 return Err(Box::new(Error::EnableExtensionNotEnabled {
3961 span: function_span,
3962 kind: crate::front::wgsl::ImplementedEnableExtension::WgpuDebugPrintf
3963 .into(),
3964 }));
3965 }
3966
3967 if arguments.is_empty() {
3968 return Err(Box::new(Error::WrongArgumentCount {
3969 expected: 1..u32::MAX,
3970 found: 0,
3971 span: function_span,
3972 }));
3973 }
3974
3975 let format_handle = arguments[0];
3977 let format = match ctx.ast_expressions[format_handle] {
3978 ast::Expression::String(s) => s.to_string(),
3979 _ => {
3980 return Err(Box::new(Error::ExpectedStringLiteral {
3981 span: ctx.ast_expressions.get_span(format_handle),
3982 description:
3983 "debugPrintf's first argument must be a string literal",
3984 }))
3985 }
3986 };
3987
3988 let mut ir_arguments = Vec::with_capacity(arguments.len().saturating_sub(1));
3990
3991 for &ast_handle in &arguments[1..] {
3992 let ir_handle = self.expression(ast_handle, ctx)?;
3993 ir_arguments.push(ir_handle);
3994 }
3995
3996 let rctx = ctx.runtime_expression_ctx(function_span)?;
3997 rctx.block
3998 .extend(rctx.emitter.finish(&rctx.function.expressions));
3999 rctx.emitter.start(&rctx.function.expressions);
4000 rctx.block.push(
4001 ir::Statement::DebugPrintf {
4002 format,
4003 arguments: ir_arguments,
4004 },
4005 function_span,
4006 );
4007
4008 return Ok(None);
4009 }
4010 _ => return Err(Box::new(Error::UnknownIdent(function_span, function_name))),
4011 }
4012 };
4013
4014 let expr = ctx.append_expression(expr, function_span)?;
4015 Ok(Some((expr, must_use)))
4016 }
4017
4018 fn call(
4037 &mut self,
4038 call_phrase: &ast::CallPhrase<'source>,
4039 span: Span,
4040 ctx: &mut ExpressionContext<'source, '_, '_>,
4041 is_statement: bool,
4042 ) -> Result<'source, Option<Handle<ir::Expression>>> {
4043 let function_name = match call_phrase.function.ident {
4044 ast::IdentExpr::Unresolved(name) => name,
4045 ast::IdentExpr::Local(_) => {
4046 return Err(Box::new(Error::CalledLocalDecl(
4047 call_phrase.function.ident_span,
4048 )))
4049 }
4050 };
4051 let mut function_span = call_phrase.function.ident_span;
4052 function_span.subsume(call_phrase.function.template_list_span);
4053 let arguments = call_phrase.arguments.as_slice();
4054
4055 let mut tl = TemplateListIter::new(function_span, &call_phrase.function.template_list);
4056
4057 let result = match ctx.globals.get(function_name) {
4058 Some(&LoweredGlobalDecl::Type(ty)) => {
4059 tl.finish(ctx)?;
4061
4062 let handle =
4063 self.construct(span, Constructor::Type(ty), function_span, arguments, ctx)?;
4064 Some((handle, MustUse::Yes))
4065 }
4066 Some(
4067 &LoweredGlobalDecl::Const(_)
4068 | &LoweredGlobalDecl::Override(_)
4069 | &LoweredGlobalDecl::Var(_),
4070 ) => {
4071 return Err(Box::new(Error::Unexpected(
4072 function_span,
4073 ExpectedToken::Function,
4074 )))
4075 }
4076 Some(&LoweredGlobalDecl::EntryPoint(_)) => {
4077 return Err(Box::new(Error::CalledEntryPoint(function_span)));
4078 }
4079 Some(&LoweredGlobalDecl::Function {
4080 handle: function,
4081 must_use,
4082 }) => {
4083 tl.finish(ctx)?;
4085
4086 let arguments = arguments
4087 .iter()
4088 .enumerate()
4089 .map(|(i, &arg)| {
4090 let Some(&ir::FunctionArgument {
4092 ty: parameter_ty, ..
4093 }) = ctx.module.functions[function].arguments.get(i)
4094 else {
4095 return self.expression(arg, ctx);
4098 };
4099
4100 let expr = self.expression_for_abstract(arg, ctx)?;
4101 ctx.try_automatic_conversions(
4102 expr,
4103 &proc::TypeResolution::Handle(parameter_ty),
4104 ctx.ast_expressions.get_span(arg),
4105 )
4106 })
4107 .collect::<Result<Vec<_>>>()?;
4108
4109 let has_result = ctx.module.functions[function].result.is_some();
4110
4111 let rctx = ctx.runtime_expression_ctx(span)?;
4112 rctx.block
4114 .extend(rctx.emitter.finish(&rctx.function.expressions));
4115 let result = has_result.then(|| {
4116 let result = rctx
4117 .function
4118 .expressions
4119 .append(ir::Expression::CallResult(function), span);
4120 rctx.local_expression_kind_tracker
4121 .insert(result, proc::ExpressionKind::Runtime);
4122 (result, must_use.into())
4123 });
4124 rctx.emitter.start(&rctx.function.expressions);
4125 rctx.block.push(
4126 ir::Statement::Call {
4127 function,
4128 arguments,
4129 result: result.map(|(expr, _)| expr),
4130 },
4131 span,
4132 );
4133
4134 result
4135 }
4136 None => {
4137 let ty = conv::map_predeclared_type(
4139 &ctx.enable_extensions,
4140 function_span,
4141 function_name,
4142 )?;
4143 if let Some(ty) = ty {
4144 let empty_template_list = call_phrase.function.template_list.is_empty();
4145 let constructor_ty = match ty {
4146 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Vector {
4147 size,
4148 }) if empty_template_list => Constructor::PartialVector { size },
4149 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Matrix {
4150 columns,
4151 rows,
4152 }) if empty_template_list => Constructor::PartialMatrix { columns, rows },
4153 conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Array)
4154 if empty_template_list =>
4155 {
4156 Constructor::PartialArray
4157 }
4158 conv::PredeclaredType::TypeGenerator(
4159 conv::TypeGenerator::CooperativeMatrix { .. },
4160 ) if empty_template_list => {
4161 return Err(Box::new(Error::UnderspecifiedCooperativeMatrix));
4162 }
4163 _ => Constructor::Type(self.finalize_type(ctx, ty, &mut tl, None)?),
4164 };
4165 tl.finish(ctx)?;
4166 let handle =
4167 self.construct(span, constructor_ty, function_span, arguments, ctx)?;
4168 Some((handle, MustUse::Yes))
4169 } else {
4170 let result = self.call_builtin(
4172 function_name,
4173 function_span,
4174 arguments,
4175 &mut tl,
4176 span,
4177 ctx,
4178 is_statement,
4179 )?;
4180 tl.finish(ctx)?;
4181 result
4182 }
4183 }
4184 };
4185
4186 let result_used = !is_statement;
4187 if matches!(result, Some((_, MustUse::Yes))) && !result_used {
4188 return Err(Box::new(Error::FunctionMustUseUnused(function_span)));
4189 }
4190 Ok(result.map(|(expr, _)| expr))
4191 }
4192
4193 fn math_function_helper(
4204 &mut self,
4205 span: Span,
4206 fun: ir::MathFunction,
4207 ast_arguments: &[Handle<ast::Expression<'source>>],
4208 ctx: &mut ExpressionContext<'source, '_, '_>,
4209 ) -> Result<'source, ir::Expression> {
4210 let mut lowered_arguments = Vec::with_capacity(ast_arguments.len());
4211 for &arg in ast_arguments {
4212 let lowered = self.expression_for_abstract(arg, ctx)?;
4213 ctx.grow_types(lowered)?;
4214 lowered_arguments.push(lowered);
4215 }
4216
4217 let fun_overloads = fun.overloads();
4218 let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?;
4219 self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?;
4220
4221 self.check_bitfield_range(span, fun, &rule, &lowered_arguments, ctx)?;
4222
4223 if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion {
4227 ctx.module.generate_predeclared_type(predeclared);
4228 }
4229
4230 Ok(ir::Expression::Math {
4231 fun,
4232 arg: lowered_arguments[0],
4233 arg1: lowered_arguments.get(1).cloned(),
4234 arg2: lowered_arguments.get(2).cloned(),
4235 arg3: lowered_arguments.get(3).cloned(),
4236 })
4237 }
4238
4239 fn check_bitfield_range(
4253 &mut self,
4254 span: Span,
4255 fun: ir::MathFunction,
4256 rule: &proc::Rule,
4257 arguments: &[Handle<ir::Expression>],
4258 ctx: &ExpressionContext<'source, '_, '_>,
4259 ) -> Result<'source, ()> {
4260 let (builtin, offset, count) = match fun {
4261 ir::MathFunction::ExtractBits => ("extractBits", 1, 2),
4262 ir::MathFunction::InsertBits => ("insertBits", 2, 3),
4263 _ => return Ok(()),
4264 };
4265
4266 let (Ok(offset), Ok(count)) = (
4267 ctx.get_const_val::<u32>(arguments[offset]),
4268 ctx.get_const_val::<u32>(arguments[count]),
4269 ) else {
4270 return Ok(());
4271 };
4272
4273 let Some(scalar) = rule.arguments[0].inner_with(&ctx.module.types).scalar() else {
4274 return Ok(());
4275 };
4276
4277 proc::check_bitfield_range(builtin, offset, count, u32::from(scalar.width) * 8)
4278 .map_err(|e| Box::new(Error::ConstantEvaluatorError(Box::new(e), span)))
4279 }
4280
4281 fn resolve_overloads<O, F>(
4292 &self,
4293 span: Span,
4294 fun: F,
4295 overloads: O,
4296 arguments: &[Handle<ir::Expression>],
4297 ctx: &ExpressionContext<'source, '_, '_>,
4298 ) -> Result<'source, proc::Rule>
4299 where
4300 O: proc::OverloadSet,
4301 F: TryToWgsl + core::fmt::Debug + Copy,
4302 {
4303 let mut remaining_overloads = overloads.clone();
4304 let min_arguments = remaining_overloads.min_arguments();
4305 let max_arguments = remaining_overloads.max_arguments();
4306 if arguments.len() < min_arguments {
4307 return Err(Box::new(Error::WrongArgumentCount {
4308 span,
4309 expected: min_arguments as u32..max_arguments as u32,
4310 found: arguments.len() as u32,
4311 }));
4312 }
4313 if arguments.len() > max_arguments {
4314 return Err(Box::new(Error::TooManyArguments {
4315 function: fun.to_wgsl_for_diagnostics(),
4316 call_span: span,
4317 arg_span: ctx.get_expression_span(arguments[max_arguments]),
4318 max_arguments: max_arguments as _,
4319 }));
4320 }
4321
4322 log::debug!(
4323 "Initial overloads: {:#?}",
4324 remaining_overloads.for_debug(&ctx.module.types)
4325 );
4326
4327 for (arg_index, &arg) in arguments.iter().enumerate() {
4328 let arg_type_resolution = &ctx.typifier()[arg];
4329 let arg_inner = arg_type_resolution.inner_with(&ctx.module.types);
4330 log::debug!(
4331 "Supplying argument {arg_index} of type {:?}",
4332 arg_type_resolution.for_debug(&ctx.module.types)
4333 );
4334 let next_remaining_overloads =
4335 remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types);
4336
4337 log::debug!(
4345 "Remaining overloads: {:#?}",
4346 next_remaining_overloads.for_debug(&ctx.module.types)
4347 );
4348
4349 if next_remaining_overloads.is_empty() {
4352 let function = fun.to_wgsl_for_diagnostics();
4353 let call_span = span;
4354 let arg_span = ctx.get_expression_span(arg);
4355 let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string();
4356
4357 let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types);
4360 if only_this_argument.is_empty() {
4361 let allowed: Vec<String> = overloads
4365 .allowed_args(arg_index, &ctx.module.to_ctx())
4366 .iter()
4367 .map(|ty| ctx.type_resolution_to_string(ty))
4368 .collect();
4369
4370 if allowed.is_empty() {
4371 unreachable!("expected all overloads to have the same arity");
4377 }
4378
4379 return Err(Box::new(Error::WrongArgumentType {
4382 function,
4383 call_span,
4384 arg_span,
4385 arg_index: arg_index as u32,
4386 arg_ty,
4387 allowed,
4388 }));
4389 }
4390
4391 let allowed: Vec<String> = remaining_overloads
4399 .allowed_args(arg_index, &ctx.module.to_ctx())
4400 .iter()
4401 .map(|ty| ctx.type_resolution_to_string(ty))
4402 .collect();
4403
4404 let mut remaining_overloads = overloads;
4407 for (prior_index, &prior_expr) in arguments.iter().enumerate() {
4408 let prior_type_resolution = &ctx.typifier()[prior_expr];
4409 let prior_ty = prior_type_resolution.inner_with(&ctx.module.types);
4410 remaining_overloads =
4411 remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types);
4412 if remaining_overloads
4413 .arg(arg_index, arg_inner, &ctx.module.types)
4414 .is_empty()
4415 {
4416 let inconsistent_span = ctx.get_expression_span(arguments[prior_index]);
4418 let inconsistent_ty =
4419 ctx.as_diagnostic_display(prior_type_resolution).to_string();
4420
4421 if allowed.is_empty() {
4422 unreachable!("expected all overloads to have the same arity");
4429 }
4430
4431 return Err(Box::new(Error::InconsistentArgumentType {
4433 function,
4434 call_span,
4435 arg_span,
4436 arg_index: arg_index as u32,
4437 arg_ty,
4438 inconsistent_span,
4439 inconsistent_index: prior_index as u32,
4440 inconsistent_ty,
4441 allowed,
4442 }));
4443 }
4444 }
4445 unreachable!("Failed to eliminate argument type when re-tried");
4446 }
4447 remaining_overloads = next_remaining_overloads;
4448 }
4449
4450 Ok(remaining_overloads.most_preferred())
4453 }
4454
4455 fn apply_automatic_conversions_for_call(
4461 &self,
4462 rule: &proc::Rule,
4463 arguments: &mut [Handle<ir::Expression>],
4464 ctx: &mut ExpressionContext<'source, '_, '_>,
4465 ) -> Result<'source, ()> {
4466 for (i, argument) in arguments.iter_mut().enumerate() {
4467 let goal_inner = rule.arguments[i].inner_with(&ctx.module.types);
4468 let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) {
4469 Some(goal_scalar) => {
4470 let arg_span = ctx.get_expression_span(*argument);
4471 ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)?
4472 }
4473 None => *argument,
4475 };
4476
4477 *argument = converted;
4478 }
4479
4480 Ok(())
4481 }
4482
4483 fn atomic_pointer(
4484 &mut self,
4485 expr: Handle<ast::Expression<'source>>,
4486 ctx: &mut ExpressionContext<'source, '_, '_>,
4487 ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> {
4488 let span = ctx.ast_expressions.get_span(expr);
4489 let pointer = self.expression(expr, ctx)?;
4490
4491 match *resolve_inner!(ctx, pointer) {
4492 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4493 ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)),
4494 ref other => {
4495 log::error!("Pointer type to {other:?} passed to atomic op");
4496 Err(Box::new(Error::InvalidAtomicPointer(span)))
4497 }
4498 },
4499 ref other => {
4500 log::error!("Type {other:?} passed to atomic op");
4501 Err(Box::new(Error::InvalidAtomicPointer(span)))
4502 }
4503 }
4504 }
4505
4506 fn atomic_helper(
4507 &mut self,
4508 span: Span,
4509 fun: ir::AtomicFunction,
4510 args: &[Handle<ast::Expression<'source>>],
4511 is_statement: bool,
4512 ctx: &mut ExpressionContext<'source, '_, '_>,
4513 ) -> Result<'source, Option<Handle<ir::Expression>>> {
4514 let mut args = ctx.prepare_args(args, 2, span);
4515
4516 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
4517 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
4518 let value_inner = resolve_inner!(ctx, value);
4519 args.finish()?;
4520
4521 let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max)
4526 && matches!(
4527 *value_inner,
4528 ir::TypeInner::Scalar(ir::Scalar { width: 8, .. })
4529 );
4530 let result = if is_64_bit_min_max && is_statement {
4531 let rctx = ctx.runtime_expression_ctx(span)?;
4532 rctx.block
4533 .extend(rctx.emitter.finish(&rctx.function.expressions));
4534 rctx.emitter.start(&rctx.function.expressions);
4535 None
4536 } else {
4537 let ty = ctx.register_type(value)?;
4538 Some(ctx.interrupt_emitter(
4539 ir::Expression::AtomicResult {
4540 ty,
4541 comparison: false,
4542 },
4543 span,
4544 )?)
4545 };
4546 let rctx = ctx.runtime_expression_ctx(span)?;
4547 rctx.block.push(
4548 ir::Statement::Atomic {
4549 pointer,
4550 fun,
4551 value,
4552 result,
4553 },
4554 span,
4555 );
4556 Ok(result)
4557 }
4558
4559 fn texture_sample_helper(
4560 &mut self,
4561 fun: Texture,
4562 args: &[Handle<ast::Expression<'source>>],
4563 span: Span,
4564 ctx: &mut ExpressionContext<'source, '_, '_>,
4565 ) -> Result<'source, ir::Expression> {
4566 let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);
4567
4568 fn get_image_and_span<'source>(
4569 lowerer: &mut Lowerer<'source, '_>,
4570 args: &mut ArgumentContext<'_, 'source>,
4571 ctx: &mut ExpressionContext<'source, '_, '_>,
4572 ) -> Result<'source, (Handle<ir::Expression>, Span)> {
4573 let image = args.next()?;
4574 let image_span = ctx.ast_expressions.get_span(image);
4575 let image = lowerer.expression_for_abstract(image, ctx)?;
4576 Ok((image, image_span))
4577 }
4578
4579 let image;
4580 let image_span;
4581 let gather;
4582 match fun {
4583 Texture::Gather => {
4584 let image_or_component = args.next()?;
4585 let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);
4586 let lowered_image_or_component = self.expression(image_or_component, ctx)?;
4588
4589 match *resolve_inner!(ctx, lowered_image_or_component) {
4590 ir::TypeInner::Image {
4591 class: ir::ImageClass::Depth { .. },
4592 ..
4593 } => {
4594 image = lowered_image_or_component;
4595 image_span = image_or_component_span;
4596 gather = Some(ir::SwizzleComponent::X);
4597 }
4598 _ => {
4599 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4600 gather = Some(ctx.gather_component(
4601 lowered_image_or_component,
4602 image_or_component_span,
4603 span,
4604 )?);
4605 }
4606 }
4607 }
4608 Texture::GatherCompare => {
4609 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4610 gather = Some(ir::SwizzleComponent::X);
4611 }
4612
4613 _ => {
4614 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4615 gather = None;
4616 }
4617 };
4618
4619 let sampler = self.expression_for_abstract(args.next()?, ctx)?;
4620
4621 let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4622 let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge);
4623
4624 let (class, arrayed) = ctx.image_data(image, image_span)?;
4625 let array_index = arrayed
4626 .then(|| self.expression(args.next()?, ctx))
4627 .transpose()?;
4628
4629 let level;
4630 let depth_ref;
4631 match fun {
4632 Texture::Gather => {
4633 level = ir::SampleLevel::Zero;
4634 depth_ref = None;
4635 }
4636 Texture::GatherCompare => {
4637 let reference =
4638 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4639 level = ir::SampleLevel::Zero;
4640 depth_ref = Some(reference);
4641 }
4642
4643 Texture::Sample => {
4644 level = ir::SampleLevel::Auto;
4645 depth_ref = None;
4646 }
4647 Texture::SampleBias => {
4648 let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4649 level = ir::SampleLevel::Bias(bias);
4650 depth_ref = None;
4651 }
4652 Texture::SampleCompare => {
4653 let reference =
4654 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4655 level = ir::SampleLevel::Auto;
4656 depth_ref = Some(reference);
4657 }
4658 Texture::SampleCompareLevel => {
4659 let reference =
4660 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4661 level = ir::SampleLevel::Zero;
4662 depth_ref = Some(reference);
4663 }
4664 Texture::SampleGrad => {
4665 let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4666 let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4667 level = ir::SampleLevel::Gradient { x, y };
4668 depth_ref = None;
4669 }
4670 Texture::SampleLevel => {
4671 let exact = match class {
4672 ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?,
4675
4676 ir::ImageClass::Sampled { .. } => {
4679 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?
4680 }
4681
4682 ir::ImageClass::Storage { .. } | ir::ImageClass::External => {
4686 self.expression(args.next()?, ctx)?
4687 }
4688 };
4689 level = ir::SampleLevel::Exact(exact);
4690 depth_ref = None;
4691 }
4692 Texture::SampleBaseClampToEdge => {
4693 level = crate::SampleLevel::Zero;
4694 depth_ref = None;
4695 }
4696 };
4697
4698 let offset = args
4699 .next()
4700 .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const()))
4701 .ok()
4702 .transpose()?;
4703
4704 args.finish()?;
4705
4706 Ok(ir::Expression::ImageSample {
4707 image,
4708 sampler,
4709 gather,
4710 coordinate,
4711 array_index,
4712 offset,
4713 level,
4714 depth_ref,
4715 clamp_to_edge,
4716 })
4717 }
4718
4719 fn subgroup_operation_helper(
4720 &mut self,
4721 span: Span,
4722 op: ir::SubgroupOperation,
4723 collective_op: ir::CollectiveOperation,
4724 arguments: &[Handle<ast::Expression<'source>>],
4725 ctx: &mut ExpressionContext<'source, '_, '_>,
4726 ) -> Result<'source, Handle<ir::Expression>> {
4727 let mut args = ctx.prepare_args(arguments, 1, span);
4728
4729 let argument = self.expression(args.next()?, ctx)?;
4730 args.finish()?;
4731
4732 let ty = ctx.register_type(argument)?;
4733
4734 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4735 let rctx = ctx.runtime_expression_ctx(span)?;
4736 rctx.block.push(
4737 ir::Statement::SubgroupCollectiveOperation {
4738 op,
4739 collective_op,
4740 argument,
4741 result,
4742 },
4743 span,
4744 );
4745 Ok(result)
4746 }
4747
4748 fn subgroup_gather_helper(
4749 &mut self,
4750 span: Span,
4751 mode: SubgroupGather,
4752 arguments: &[Handle<ast::Expression<'source>>],
4753 ctx: &mut ExpressionContext<'source, '_, '_>,
4754 ) -> Result<'source, Handle<ir::Expression>> {
4755 let mut args = ctx.prepare_args(arguments, 2, span);
4756
4757 let argument = self.expression(args.next()?, ctx)?;
4758
4759 use SubgroupGather as Sg;
4760 let mode = if let Sg::BroadcastFirst = mode {
4761 ir::GatherMode::BroadcastFirst
4762 } else {
4763 let index = self.expression(args.next()?, ctx)?;
4764 match mode {
4765 Sg::BroadcastFirst => unreachable!(),
4766 Sg::Broadcast => ir::GatherMode::Broadcast(index),
4767 Sg::Shuffle => ir::GatherMode::Shuffle(index),
4768 Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index),
4769 Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index),
4770 Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index),
4771 Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index),
4772 }
4773 };
4774
4775 args.finish()?;
4776
4777 let ty = ctx.register_type(argument)?;
4778
4779 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4780 let rctx = ctx.runtime_expression_ctx(span)?;
4781 rctx.block.push(
4782 ir::Statement::SubgroupGather {
4783 mode,
4784 argument,
4785 result,
4786 },
4787 span,
4788 );
4789 Ok(result)
4790 }
4791
4792 fn r#struct(
4793 &mut self,
4794 s: &ast::Struct<'source>,
4795 span: Span,
4796 ctx: &mut GlobalContext<'source, '_, '_>,
4797 ) -> Result<'source, Handle<ir::Type>> {
4798 let mut offset = 0;
4799 let mut struct_alignment = proc::Alignment::ONE;
4800 let mut members = Vec::with_capacity(s.members.len());
4801
4802 let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new();
4803
4804 for member in s.members.iter() {
4805 let ty = self.resolve_ast_type(&member.ty, &mut ctx.as_const())?;
4806
4807 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
4808 let LayoutErrorInner::TooLarge = err.inner else {
4809 unreachable!("unexpected layout error: {err:?}");
4810 };
4811 if ty == err.ty {
4817 Box::new(Error::StructMemberTooLarge {
4818 member_name_span: member.name.span,
4819 })
4820 } else {
4821 Box::new(Error::TypeTooLarge {
4824 span: ctx.module.types.get_span(err.ty),
4825 })
4826 }
4827 })?;
4828
4829 let member_min_size = ctx.layouter[ty].size;
4830 let member_min_alignment = ctx.layouter[ty].alignment;
4831
4832 let member_size = if let Some(size_expr) = member.size {
4833 let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;
4834 if let ir::TypeInner::Array {
4835 size: ir::ArraySize::Dynamic | ir::ArraySize::Pending(_),
4836 ..
4837 } = ctx.module.types[ty].inner
4838 {
4839 return Err(Box::new(Error::SizeAttributeRequiresFixedFootprint(span)));
4840 }
4841 if size < member_min_size {
4842 return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size)));
4843 } else {
4844 size
4845 }
4846 } else {
4847 member_min_size
4848 };
4849
4850 let member_alignment = if let Some(align_expr) = member.align {
4851 let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;
4852 if let Some(alignment) = proc::Alignment::new(align) {
4853 if alignment < member_min_alignment {
4854 return Err(Box::new(Error::AlignAttributeTooLow(
4855 span,
4856 member_min_alignment,
4857 )));
4858 } else {
4859 alignment
4860 }
4861 } else {
4862 return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span)));
4863 }
4864 } else {
4865 member_min_alignment
4866 };
4867
4868 let binding = self.binding(&member.binding, ty, ctx)?;
4869
4870 offset = member_alignment.round_up(offset);
4871 struct_alignment = struct_alignment.max(member_alignment);
4872
4873 if !member.doc_comments.is_empty() {
4874 doc_comments.push(Some(
4875 member.doc_comments.iter().map(|s| s.to_string()).collect(),
4876 ));
4877 } else {
4878 doc_comments.push(None);
4879 }
4880 members.push(ir::StructMember {
4881 name: Some(member.name.name.to_owned()),
4882 ty,
4883 binding,
4884 offset,
4885 });
4886
4887 offset += member_size;
4888 if offset > crate::valid::MAX_TYPE_SIZE {
4889 return Err(Box::new(Error::TypeTooLarge { span }));
4890 }
4891 }
4892
4893 let size = struct_alignment.round_up(offset);
4894 let inner = ir::TypeInner::Struct {
4895 members,
4896 span: size,
4897 };
4898
4899 let handle = ctx.module.types.insert(
4900 ir::Type {
4901 name: Some(s.name.name.to_string()),
4902 inner,
4903 },
4904 span,
4905 );
4906 for (i, c) in doc_comments.drain(..).enumerate() {
4907 if let Some(comment) = c {
4908 ctx.module
4909 .get_or_insert_default_doc_comments()
4910 .struct_members
4911 .insert((handle, i), comment);
4912 }
4913 }
4914 Ok(handle)
4915 }
4916
4917 fn const_u32(
4918 &mut self,
4919 expr: Handle<ast::Expression<'source>>,
4920 ctx: &mut ExpressionContext<'source, '_, '_>,
4921 ) -> Result<'source, (u32, Span)> {
4922 let span = ctx.ast_expressions.get_span(expr);
4923 let expr = self.expression(expr, ctx)?;
4924 let value = ctx
4925 .module
4926 .to_ctx()
4927 .get_const_val(expr)
4928 .map_err(|err| match err {
4929 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4930 Error::ExpectedConstExprConcreteIntegerScalar(span)
4931 }
4932 proc::ConstValueError::Negative => Error::ExpectedNonNegative(span),
4933 })?;
4934 Ok((value, span))
4935 }
4936
4937 fn array_size(
4938 &mut self,
4939 expr: Handle<ast::Expression<'source>>,
4940 ctx: &mut ExpressionContext<'source, '_, '_>,
4941 ) -> Result<'source, ir::ArraySize> {
4942 let span = ctx.ast_expressions.get_span(expr);
4943 let const_ctx = &mut ctx.as_const();
4944 let const_expr = self.expression(expr, const_ctx);
4945 match const_expr {
4946 Ok(value) => {
4947 let len = const_ctx.get_const_val(value).map_err(|err| {
4948 Box::new(match err {
4949 proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4950 Error::ExpectedConstExprConcreteIntegerScalar(span)
4951 }
4952 proc::ConstValueError::Negative => Error::ExpectedPositiveArrayLength(span),
4953 })
4954 })?;
4955 let size = NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;
4956 Ok(ir::ArraySize::Constant(size))
4957 }
4958 Err(err) => {
4959 let Error::ConstantEvaluatorError(ref ty, _) = *err else {
4962 return Err(err);
4963 };
4964
4965 let proc::ConstantEvaluatorError::OverrideExpr = **ty else {
4966 return Err(err);
4967 };
4968
4969 Ok(ir::ArraySize::Pending(self.array_size_override(
4970 expr,
4971 &mut ctx.as_global().as_override(),
4972 span,
4973 )?))
4974 }
4975 }
4976 }
4977
4978 fn array_size_override(
4979 &mut self,
4980 size_expr: Handle<ast::Expression<'source>>,
4981 ctx: &mut ExpressionContext<'source, '_, '_>,
4982 span: Span,
4983 ) -> Result<'source, Handle<ir::Override>> {
4984 let expr = self.expression(size_expr, ctx)?;
4985 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
4986 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({
4987 if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] {
4988 handle
4989 } else {
4990 let ty = ctx.register_type(expr)?;
4991 ctx.module.overrides.append(
4992 ir::Override {
4993 name: None,
4994 id: None,
4995 ty,
4996 init: Some(expr),
4997 },
4998 span,
4999 )
5000 }
5001 }),
5002 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
5003 span,
5004 ))),
5005 }
5006 }
5007
5008 fn resolve_named_ast_type(
5018 &mut self,
5019 ident: &ast::TemplateElaboratedIdent<'source>,
5020 name: String,
5021 ctx: &mut ExpressionContext<'source, '_, '_>,
5022 ) -> Result<'source, Handle<ir::Type>> {
5023 self.type_specifier(ident, ctx, Some(name))
5024 }
5025
5026 fn resolve_ast_type(
5028 &mut self,
5029 ident: &ast::TemplateElaboratedIdent<'source>,
5030 ctx: &mut ExpressionContext<'source, '_, '_>,
5031 ) -> Result<'source, Handle<ir::Type>> {
5032 self.type_specifier(ident, ctx, None)
5033 }
5034
5035 fn binding(
5036 &mut self,
5037 binding: &Option<ast::Binding<'source>>,
5038 ty: Handle<ir::Type>,
5039 ctx: &mut GlobalContext<'source, '_, '_>,
5040 ) -> Result<'source, Option<ir::Binding>> {
5041 Ok(match *binding {
5042 Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)),
5043 Some(ast::Binding::Location {
5044 location,
5045 interpolation,
5046 sampling,
5047 blend_src,
5048 per_primitive,
5049 }) => {
5050 let blend_src = if let Some(blend_src) = blend_src {
5051 Some(self.const_u32(blend_src, &mut ctx.as_const())?.0)
5052 } else {
5053 None
5054 };
5055
5056 let mut binding = ir::Binding::Location {
5057 location: self.const_u32(location, &mut ctx.as_const())?.0,
5058 interpolation,
5059 sampling,
5060 blend_src,
5061 per_primitive,
5062 };
5063 binding.apply_default_interpolation(&ctx.module.types[ty].inner);
5064 Some(binding)
5065 }
5066 None => None,
5067 })
5068 }
5069
5070 fn ray_query_pointer(
5071 &mut self,
5072 expr: Handle<ast::Expression<'source>>,
5073 ctx: &mut ExpressionContext<'source, '_, '_>,
5074 ) -> Result<'source, Handle<ir::Expression>> {
5075 let span = ctx.ast_expressions.get_span(expr);
5076 let pointer = self.expression(expr, ctx)?;
5077
5078 match *resolve_inner!(ctx, pointer) {
5079 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
5080 ir::TypeInner::RayQuery { .. } => Ok(pointer),
5081 ref other => {
5082 log::error!("Pointer type to {other:?} passed to ray query op");
5083 Err(Box::new(Error::InvalidRayQueryPointer(span)))
5084 }
5085 },
5086 ref other => {
5087 log::error!("Type {other:?} passed to ray query op");
5088 Err(Box::new(Error::InvalidRayQueryPointer(span)))
5089 }
5090 }
5091 }
5092}
5093
5094impl ir::AtomicFunction {
5095 pub fn map(word: &str) -> Option<Self> {
5096 Some(match word {
5097 "atomicAdd" => ir::AtomicFunction::Add,
5098 "atomicSub" => ir::AtomicFunction::Subtract,
5099 "atomicAnd" => ir::AtomicFunction::And,
5100 "atomicOr" => ir::AtomicFunction::InclusiveOr,
5101 "atomicXor" => ir::AtomicFunction::ExclusiveOr,
5102 "atomicMin" => ir::AtomicFunction::Min,
5103 "atomicMax" => ir::AtomicFunction::Max,
5104 "atomicExchange" => ir::AtomicFunction::Exchange { compare: None },
5105 _ => return None,
5106 })
5107 }
5108}