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