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