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