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 const_eval_expr_to_u32(
539 &self,
540 handle: Handle<ir::Expression>,
541 ) -> core::result::Result<u32, proc::U32EvalError> {
542 match self.expr_type {
543 ExpressionContextType::Runtime(ref ctx) => {
544 if !ctx.local_expression_kind_tracker.is_const(handle) {
545 return Err(proc::U32EvalError::NonConst);
546 }
547
548 self.module
549 .to_ctx()
550 .eval_expr_to_u32_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 .eval_expr_to_u32_from(handle, &ctx.function.expressions)
557 }
558 ExpressionContextType::Constant(None) => self.module.to_ctx().eval_expr_to_u32(handle),
559 ExpressionContextType::Override => Err(proc::U32EvalError::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 .eval_expr_to_u32_from(expr, &rctx.function.expressions)
698 .map_err(|err| match err {
699 proc::U32EvalError::NonConst => {
700 Error::ExpectedConstExprConcreteIntegerScalar(component_span)
701 }
702 proc::U32EvalError::Negative => Error::ExpectedNonNegative(component_span),
703 })?;
704 ir::SwizzleComponent::XYZW
705 .get(index as usize)
706 .copied()
707 .ok_or(Box::new(Error::InvalidGatherComponent(component_span)))
708 }
709 ExpressionContextType::Constant(_) | ExpressionContextType::Override => Err(Box::new(
712 Error::UnexpectedOperationInConstContext(gather_span),
713 )),
714 }
715 }
716
717 fn register_type(
728 &mut self,
729 handle: Handle<ir::Expression>,
730 ) -> Result<'source, Handle<ir::Type>> {
731 self.grow_types(handle)?;
732 let typifier = match self.expr_type {
736 ExpressionContextType::Runtime(ref ctx)
737 | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,
738 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
739 &*self.const_typifier
740 }
741 };
742 Ok(typifier.register_type(handle, &mut self.module.types))
743 }
744
745 fn grow_types(&mut self, handle: Handle<ir::Expression>) -> Result<'source, &mut Self> {
766 let empty_arena = Arena::new();
767 let resolve_ctx;
768 let typifier;
769 let expressions;
770 match self.expr_type {
771 ExpressionContextType::Runtime(ref mut ctx)
772 | ExpressionContextType::Constant(Some(ref mut ctx)) => {
773 resolve_ctx = proc::ResolveContext::with_locals(
774 self.module,
775 &ctx.function.local_variables,
776 &ctx.function.arguments,
777 );
778 typifier = &mut *ctx.typifier;
779 expressions = &ctx.function.expressions;
780 }
781 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
782 resolve_ctx = proc::ResolveContext::with_locals(self.module, &empty_arena, &[]);
783 typifier = self.const_typifier;
784 expressions = &self.module.global_expressions;
785 }
786 };
787 typifier
788 .grow(handle, expressions, &resolve_ctx)
789 .map_err(Error::InvalidResolve)?;
790
791 Ok(self)
792 }
793
794 fn image_data(
795 &mut self,
796 image: Handle<ir::Expression>,
797 span: Span,
798 ) -> Result<'source, (ir::ImageClass, bool)> {
799 match *resolve_inner!(self, image) {
800 ir::TypeInner::Image { class, arrayed, .. } => Ok((class, arrayed)),
801 _ => Err(Box::new(Error::BadTexture(span))),
802 }
803 }
804
805 fn prepare_args<'b>(
806 &mut self,
807 args: &'b [Handle<ast::Expression<'source>>],
808 min_args: u32,
809 span: Span,
810 ) -> ArgumentContext<'b, 'source> {
811 ArgumentContext {
812 args: args.iter(),
813 min_args,
814 args_used: 0,
815 total_args: args.len() as u32,
816 span,
817 }
818 }
819
820 fn binary_op_splat(
828 &mut self,
829 op: ir::BinaryOperator,
830 left: &mut Handle<ir::Expression>,
831 right: &mut Handle<ir::Expression>,
832 ) -> Result<'source, ()> {
833 if matches!(
834 op,
835 ir::BinaryOperator::Add
836 | ir::BinaryOperator::Subtract
837 | ir::BinaryOperator::Divide
838 | ir::BinaryOperator::Modulo
839 ) {
840 match resolve_inner_binary!(self, *left, *right) {
841 (&ir::TypeInner::Vector { size, .. }, &ir::TypeInner::Scalar { .. }) => {
842 *right = self.append_expression(
843 ir::Expression::Splat {
844 size,
845 value: *right,
846 },
847 self.get_expression_span(*right),
848 )?;
849 }
850 (&ir::TypeInner::Scalar { .. }, &ir::TypeInner::Vector { size, .. }) => {
851 *left = self.append_expression(
852 ir::Expression::Splat { size, value: *left },
853 self.get_expression_span(*left),
854 )?;
855 }
856 _ => {}
857 }
858 }
859
860 Ok(())
861 }
862
863 fn interrupt_emitter(
868 &mut self,
869 expression: ir::Expression,
870 span: Span,
871 ) -> Result<'source, Handle<ir::Expression>> {
872 match self.expr_type {
873 ExpressionContextType::Runtime(ref mut rctx)
874 | ExpressionContextType::Constant(Some(ref mut rctx)) => {
875 rctx.block
876 .extend(rctx.emitter.finish(&rctx.function.expressions));
877 }
878 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}
879 }
880 let result = self.append_expression(expression, span);
881 match self.expr_type {
882 ExpressionContextType::Runtime(ref mut rctx)
883 | ExpressionContextType::Constant(Some(ref mut rctx)) => {
884 rctx.emitter.start(&rctx.function.expressions);
885 }
886 ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}
887 }
888 result
889 }
890
891 fn apply_load_rule(
896 &mut self,
897 expr: Typed<Handle<ir::Expression>>,
898 ) -> Result<'source, Handle<ir::Expression>> {
899 match expr {
900 Typed::Reference(pointer) => {
901 let load = ir::Expression::Load { pointer };
902 let span = self.get_expression_span(pointer);
903 self.append_expression(load, span)
904 }
905 Typed::Plain(handle) => Ok(handle),
906 }
907 }
908
909 fn ensure_type_exists(&mut self, inner: ir::TypeInner) -> Handle<ir::Type> {
910 self.as_global().ensure_type_exists(None, inner)
911 }
912}
913
914struct ArgumentContext<'ctx, 'source> {
915 args: core::slice::Iter<'ctx, Handle<ast::Expression<'source>>>,
916 min_args: u32,
917 args_used: u32,
918 total_args: u32,
919 span: Span,
920}
921
922impl<'source> ArgumentContext<'_, 'source> {
923 pub fn finish(self) -> Result<'source, ()> {
924 if self.args.len() == 0 {
925 Ok(())
926 } else {
927 Err(Box::new(Error::WrongArgumentCount {
928 found: self.total_args,
929 expected: self.min_args..self.args_used + 1,
930 span: self.span,
931 }))
932 }
933 }
934
935 pub fn next(&mut self) -> Result<'source, Handle<ast::Expression<'source>>> {
936 match self.args.next().copied() {
937 Some(arg) => {
938 self.args_used += 1;
939 Ok(arg)
940 }
941 None => Err(Box::new(Error::WrongArgumentCount {
942 found: self.total_args,
943 expected: self.min_args..self.args_used + 1,
944 span: self.span,
945 })),
946 }
947 }
948}
949
950#[derive(Debug, Copy, Clone)]
951enum Declared<T> {
952 Const(T),
954
955 Runtime(T),
957}
958
959impl<T> Declared<T> {
960 fn runtime(self) -> T {
961 match self {
962 Declared::Const(t) | Declared::Runtime(t) => t,
963 }
964 }
965
966 fn const_time(self) -> Option<T> {
967 match self {
968 Declared::Const(t) => Some(t),
969 Declared::Runtime(_) => None,
970 }
971 }
972}
973
974#[derive(Debug, Copy, Clone)]
996enum Typed<T> {
997 Reference(T),
999
1000 Plain(T),
1002}
1003
1004impl<T> Typed<T> {
1005 fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> {
1006 match self {
1007 Self::Reference(v) => Typed::Reference(f(v)),
1008 Self::Plain(v) => Typed::Plain(f(v)),
1009 }
1010 }
1011
1012 fn try_map<U, E>(
1013 self,
1014 mut f: impl FnMut(T) -> core::result::Result<U, E>,
1015 ) -> core::result::Result<Typed<U>, E> {
1016 Ok(match self {
1017 Self::Reference(expr) => Typed::Reference(f(expr)?),
1018 Self::Plain(expr) => Typed::Plain(f(expr)?),
1019 })
1020 }
1021}
1022
1023enum Components {
1029 Single(u32),
1030 Swizzle {
1031 size: ir::VectorSize,
1032 pattern: [ir::SwizzleComponent; 4],
1033 },
1034}
1035
1036impl Components {
1037 const fn letter_component(letter: char) -> Option<ir::SwizzleComponent> {
1038 use ir::SwizzleComponent as Sc;
1039 match letter {
1040 'x' | 'r' => Some(Sc::X),
1041 'y' | 'g' => Some(Sc::Y),
1042 'z' | 'b' => Some(Sc::Z),
1043 'w' | 'a' => Some(Sc::W),
1044 _ => None,
1045 }
1046 }
1047
1048 fn single_component(name: &str, name_span: Span) -> Result<'_, u32> {
1049 let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?;
1050 match Self::letter_component(ch) {
1051 Some(sc) => Ok(sc as u32),
1052 None => Err(Box::new(Error::BadAccessor(name_span))),
1053 }
1054 }
1055
1056 fn new(name: &str, name_span: Span) -> Result<'_, Self> {
1060 let size = match name.len() {
1061 1 => return Ok(Components::Single(Self::single_component(name, name_span)?)),
1062 2 => ir::VectorSize::Bi,
1063 3 => ir::VectorSize::Tri,
1064 4 => ir::VectorSize::Quad,
1065 _ => return Err(Box::new(Error::BadAccessor(name_span))),
1066 };
1067
1068 let mut pattern = [ir::SwizzleComponent::X; 4];
1069 for (comp, ch) in pattern.iter_mut().zip(name.chars()) {
1070 *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?;
1071 }
1072
1073 if name.chars().all(|c| matches!(c, 'x' | 'y' | 'z' | 'w'))
1074 || name.chars().all(|c| matches!(c, 'r' | 'g' | 'b' | 'a'))
1075 {
1076 Ok(Components::Swizzle { size, pattern })
1077 } else {
1078 Err(Box::new(Error::BadAccessor(name_span)))
1079 }
1080 }
1081}
1082
1083enum LoweredGlobalDecl {
1085 Function {
1086 handle: Handle<ir::Function>,
1087 must_use: bool,
1088 },
1089 Var(Handle<ir::GlobalVariable>),
1090 Const(Handle<ir::Constant>),
1091 Override(Handle<ir::Override>),
1092 Type(Handle<ir::Type>),
1093 EntryPoint(usize),
1094}
1095
1096enum Texture {
1097 Gather,
1098 GatherCompare,
1099
1100 Sample,
1101 SampleBias,
1102 SampleCompare,
1103 SampleCompareLevel,
1104 SampleGrad,
1105 SampleLevel,
1106 SampleBaseClampToEdge,
1107}
1108
1109impl Texture {
1110 pub fn map(word: &str) -> Option<Self> {
1111 Some(match word {
1112 "textureGather" => Self::Gather,
1113 "textureGatherCompare" => Self::GatherCompare,
1114
1115 "textureSample" => Self::Sample,
1116 "textureSampleBias" => Self::SampleBias,
1117 "textureSampleCompare" => Self::SampleCompare,
1118 "textureSampleCompareLevel" => Self::SampleCompareLevel,
1119 "textureSampleGrad" => Self::SampleGrad,
1120 "textureSampleLevel" => Self::SampleLevel,
1121 "textureSampleBaseClampToEdge" => Self::SampleBaseClampToEdge,
1122 _ => return None,
1123 })
1124 }
1125
1126 pub const fn min_argument_count(&self) -> u32 {
1127 match *self {
1128 Self::Gather => 3,
1129 Self::GatherCompare => 4,
1130
1131 Self::Sample => 3,
1132 Self::SampleBias => 5,
1133 Self::SampleCompare => 5,
1134 Self::SampleCompareLevel => 5,
1135 Self::SampleGrad => 6,
1136 Self::SampleLevel => 5,
1137 Self::SampleBaseClampToEdge => 3,
1138 }
1139 }
1140}
1141
1142enum SubgroupGather {
1143 BroadcastFirst,
1144 Broadcast,
1145 Shuffle,
1146 ShuffleDown,
1147 ShuffleUp,
1148 ShuffleXor,
1149 QuadBroadcast,
1150}
1151
1152impl SubgroupGather {
1153 pub fn map(word: &str) -> Option<Self> {
1154 Some(match word {
1155 "subgroupBroadcastFirst" => Self::BroadcastFirst,
1156 "subgroupBroadcast" => Self::Broadcast,
1157 "subgroupShuffle" => Self::Shuffle,
1158 "subgroupShuffleDown" => Self::ShuffleDown,
1159 "subgroupShuffleUp" => Self::ShuffleUp,
1160 "subgroupShuffleXor" => Self::ShuffleXor,
1161 "quadBroadcast" => Self::QuadBroadcast,
1162 _ => return None,
1163 })
1164 }
1165}
1166
1167enum AbstractRule {
1169 Concretize,
1171
1172 Allow,
1174}
1175
1176pub struct Lowerer<'source, 'temp> {
1177 index: &'temp Index<'source>,
1178}
1179
1180impl<'source, 'temp> Lowerer<'source, 'temp> {
1181 pub const fn new(index: &'temp Index<'source>) -> Self {
1182 Self { index }
1183 }
1184
1185 pub fn lower(&mut self, tu: ast::TranslationUnit<'source>) -> Result<'source, ir::Module> {
1186 let mut module = ir::Module {
1187 diagnostic_filters: tu.diagnostic_filters,
1188 diagnostic_filter_leaf: tu.diagnostic_filter_leaf,
1189 ..Default::default()
1190 };
1191
1192 let mut ctx = GlobalContext {
1193 ast_expressions: &tu.expressions,
1194 globals: &mut FastHashMap::default(),
1195 types: &tu.types,
1196 module: &mut module,
1197 const_typifier: &mut Typifier::new(),
1198 layouter: &mut proc::Layouter::default(),
1199 global_expression_kind_tracker: &mut proc::ExpressionKindTracker::new(),
1200 };
1201 if !tu.doc_comments.is_empty() {
1202 ctx.module.get_or_insert_default_doc_comments().module =
1203 tu.doc_comments.iter().map(|s| s.to_string()).collect();
1204 }
1205
1206 for decl_handle in self.index.visit_ordered() {
1207 let span = tu.decls.get_span(decl_handle);
1208 let decl = &tu.decls[decl_handle];
1209
1210 match decl.kind {
1211 ast::GlobalDeclKind::Fn(ref f) => {
1212 let lowered_decl = self.function(f, span, &mut ctx)?;
1213 if !f.doc_comments.is_empty() {
1214 match lowered_decl {
1215 LoweredGlobalDecl::Function { handle, .. } => {
1216 ctx.module
1217 .get_or_insert_default_doc_comments()
1218 .functions
1219 .insert(
1220 handle,
1221 f.doc_comments.iter().map(|s| s.to_string()).collect(),
1222 );
1223 }
1224 LoweredGlobalDecl::EntryPoint(index) => {
1225 ctx.module
1226 .get_or_insert_default_doc_comments()
1227 .entry_points
1228 .insert(
1229 index,
1230 f.doc_comments.iter().map(|s| s.to_string()).collect(),
1231 );
1232 }
1233 _ => {}
1234 }
1235 }
1236 ctx.globals.insert(f.name.name, lowered_decl);
1237 }
1238 ast::GlobalDeclKind::Var(ref v) => {
1239 let explicit_ty =
1240 v.ty.map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1241 .transpose()?;
1242
1243 let (ty, initializer) = self.type_and_init(
1244 v.name,
1245 v.init,
1246 explicit_ty,
1247 AbstractRule::Concretize,
1248 &mut ctx.as_override(),
1249 )?;
1250
1251 let binding = if let Some(ref binding) = v.binding {
1252 Some(ir::ResourceBinding {
1253 group: self.const_u32(binding.group, &mut ctx.as_const())?.0,
1254 binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0,
1255 })
1256 } else {
1257 None
1258 };
1259
1260 let handle = ctx.module.global_variables.append(
1261 ir::GlobalVariable {
1262 name: Some(v.name.name.to_string()),
1263 space: v.space,
1264 binding,
1265 ty,
1266 init: initializer,
1267 },
1268 span,
1269 );
1270
1271 if !v.doc_comments.is_empty() {
1272 ctx.module
1273 .get_or_insert_default_doc_comments()
1274 .global_variables
1275 .insert(
1276 handle,
1277 v.doc_comments.iter().map(|s| s.to_string()).collect(),
1278 );
1279 }
1280 ctx.globals
1281 .insert(v.name.name, LoweredGlobalDecl::Var(handle));
1282 }
1283 ast::GlobalDeclKind::Const(ref c) => {
1284 let mut ectx = ctx.as_const();
1285
1286 let explicit_ty =
1287 c.ty.map(|ast| self.resolve_ast_type(ast, &mut ectx))
1288 .transpose()?;
1289
1290 let (ty, init) = self.type_and_init(
1291 c.name,
1292 Some(c.init),
1293 explicit_ty,
1294 AbstractRule::Allow,
1295 &mut ectx,
1296 )?;
1297 let init = init.expect("Global const must have init");
1298
1299 let handle = ctx.module.constants.append(
1300 ir::Constant {
1301 name: Some(c.name.name.to_string()),
1302 ty,
1303 init,
1304 },
1305 span,
1306 );
1307
1308 ctx.globals
1309 .insert(c.name.name, LoweredGlobalDecl::Const(handle));
1310 if !c.doc_comments.is_empty() {
1311 ctx.module
1312 .get_or_insert_default_doc_comments()
1313 .constants
1314 .insert(
1315 handle,
1316 c.doc_comments.iter().map(|s| s.to_string()).collect(),
1317 );
1318 }
1319 }
1320 ast::GlobalDeclKind::Override(ref o) => {
1321 let explicit_ty =
1322 o.ty.map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1323 .transpose()?;
1324
1325 let mut ectx = ctx.as_override();
1326
1327 let (ty, init) = self.type_and_init(
1328 o.name,
1329 o.init,
1330 explicit_ty,
1331 AbstractRule::Concretize,
1332 &mut ectx,
1333 )?;
1334
1335 let id =
1336 o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))
1337 .transpose()?;
1338
1339 let id = if let Some((id, id_span)) = id {
1340 Some(
1341 u16::try_from(id)
1342 .map_err(|_| Error::PipelineConstantIDValue(id_span))?,
1343 )
1344 } else {
1345 None
1346 };
1347
1348 let handle = ctx.module.overrides.append(
1349 ir::Override {
1350 name: Some(o.name.name.to_string()),
1351 id,
1352 ty,
1353 init,
1354 },
1355 span,
1356 );
1357
1358 ctx.globals
1359 .insert(o.name.name, LoweredGlobalDecl::Override(handle));
1360 }
1361 ast::GlobalDeclKind::Struct(ref s) => {
1362 let handle = self.r#struct(s, span, &mut ctx)?;
1363 ctx.globals
1364 .insert(s.name.name, LoweredGlobalDecl::Type(handle));
1365 if !s.doc_comments.is_empty() {
1366 ctx.module
1367 .get_or_insert_default_doc_comments()
1368 .types
1369 .insert(
1370 handle,
1371 s.doc_comments.iter().map(|s| s.to_string()).collect(),
1372 );
1373 }
1374 }
1375 ast::GlobalDeclKind::Type(ref alias) => {
1376 let ty = self.resolve_named_ast_type(
1377 alias.ty,
1378 Some(alias.name.name.to_string()),
1379 &mut ctx.as_const(),
1380 )?;
1381 ctx.globals
1382 .insert(alias.name.name, LoweredGlobalDecl::Type(ty));
1383 }
1384 ast::GlobalDeclKind::ConstAssert(condition) => {
1385 let condition = self.expression(condition, &mut ctx.as_const())?;
1386
1387 let span = ctx.module.global_expressions.get_span(condition);
1388 match ctx
1389 .module
1390 .to_ctx()
1391 .eval_expr_to_bool_from(condition, &ctx.module.global_expressions)
1392 {
1393 Some(true) => Ok(()),
1394 Some(false) => Err(Error::ConstAssertFailed(span)),
1395 _ => Err(Error::NotBool(span)),
1396 }?;
1397 }
1398 }
1399 }
1400
1401 crate::compact::compact(&mut module, KeepUnused::Yes);
1405
1406 Ok(module)
1407 }
1408
1409 fn type_and_init(
1411 &mut self,
1412 name: ast::Ident<'source>,
1413 init: Option<Handle<ast::Expression<'source>>>,
1414 explicit_ty: Option<Handle<ir::Type>>,
1415 abstract_rule: AbstractRule,
1416 ectx: &mut ExpressionContext<'source, '_, '_>,
1417 ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> {
1418 let ty;
1419 let initializer;
1420 match (init, explicit_ty) {
1421 (Some(init), Some(explicit_ty)) => {
1422 let init = self.expression_for_abstract(init, ectx)?;
1423 let ty_res = proc::TypeResolution::Handle(explicit_ty);
1424 let init = ectx
1425 .try_automatic_conversions(init, &ty_res, name.span)
1426 .map_err(|error| match *error {
1427 Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch {
1428 name: name.span,
1429 expected: e.dest_type,
1430 got: e.source_type,
1431 }),
1432 _ => error,
1433 })?;
1434
1435 let init_ty = ectx.register_type(init)?;
1436 if !ectx.module.compare_types(
1437 &proc::TypeResolution::Handle(explicit_ty),
1438 &proc::TypeResolution::Handle(init_ty),
1439 ) {
1440 return Err(Box::new(Error::InitializationTypeMismatch {
1441 name: name.span,
1442 expected: ectx.type_to_string(explicit_ty),
1443 got: ectx.type_to_string(init_ty),
1444 }));
1445 }
1446 ty = explicit_ty;
1447 initializer = Some(init);
1448 }
1449 (Some(init), None) => {
1450 let mut init = self.expression_for_abstract(init, ectx)?;
1451 if let AbstractRule::Concretize = abstract_rule {
1452 init = ectx.concretize(init)?;
1453 }
1454 ty = ectx.register_type(init)?;
1455 initializer = Some(init);
1456 }
1457 (None, Some(explicit_ty)) => {
1458 ty = explicit_ty;
1459 initializer = None;
1460 }
1461 (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))),
1462 }
1463 Ok((ty, initializer))
1464 }
1465
1466 fn function(
1467 &mut self,
1468 f: &ast::Function<'source>,
1469 span: Span,
1470 ctx: &mut GlobalContext<'source, '_, '_>,
1471 ) -> Result<'source, LoweredGlobalDecl> {
1472 let mut local_table = FastHashMap::default();
1473 let mut expressions = Arena::new();
1474 let mut named_expressions = FastIndexMap::default();
1475 let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new();
1476
1477 let arguments = f
1478 .arguments
1479 .iter()
1480 .enumerate()
1481 .map(|(i, arg)| -> Result<'_, _> {
1482 let ty = self.resolve_ast_type(arg.ty, &mut ctx.as_const())?;
1483 let expr =
1484 expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span);
1485 local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr)));
1486 named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));
1487 local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime);
1488
1489 Ok(ir::FunctionArgument {
1490 name: Some(arg.name.name.to_string()),
1491 ty,
1492 binding: self.binding(&arg.binding, ty, ctx)?,
1493 })
1494 })
1495 .collect::<Result<Vec<_>>>()?;
1496
1497 let result = f
1498 .result
1499 .as_ref()
1500 .map(|res| -> Result<'_, _> {
1501 let ty = self.resolve_ast_type(res.ty, &mut ctx.as_const())?;
1502 Ok(ir::FunctionResult {
1503 ty,
1504 binding: self.binding(&res.binding, ty, ctx)?,
1505 })
1506 })
1507 .transpose()?;
1508
1509 let mut function = ir::Function {
1510 name: Some(f.name.name.to_string()),
1511 arguments,
1512 result,
1513 local_variables: Arena::new(),
1514 expressions,
1515 named_expressions: crate::NamedExpressions::default(),
1516 body: ir::Block::default(),
1517 diagnostic_filter_leaf: f.diagnostic_filter_leaf,
1518 };
1519
1520 let mut typifier = Typifier::default();
1521 let mut stmt_ctx = StatementContext {
1522 local_table: &mut local_table,
1523 globals: ctx.globals,
1524 ast_expressions: ctx.ast_expressions,
1525 const_typifier: ctx.const_typifier,
1526 typifier: &mut typifier,
1527 layouter: ctx.layouter,
1528 function: &mut function,
1529 named_expressions: &mut named_expressions,
1530 types: ctx.types,
1531 module: ctx.module,
1532 local_expression_kind_tracker: &mut local_expression_kind_tracker,
1533 global_expression_kind_tracker: ctx.global_expression_kind_tracker,
1534 };
1535 let mut body = self.block(&f.body, false, &mut stmt_ctx)?;
1536 proc::ensure_block_returns(&mut body);
1537
1538 function.body = body;
1539 function.named_expressions = named_expressions
1540 .into_iter()
1541 .map(|(key, (name, _))| (key, name))
1542 .collect();
1543
1544 if let Some(ref entry) = f.entry_point {
1545 let (workgroup_size, workgroup_size_overrides) =
1546 if let Some(workgroup_size) = entry.workgroup_size {
1547 let mut workgroup_size_out = [1; 3];
1549 let mut workgroup_size_overrides_out = [None; 3];
1550 for (i, size) in workgroup_size.into_iter().enumerate() {
1551 if let Some(size_expr) = size {
1552 match self.const_u32(size_expr, &mut ctx.as_const()) {
1553 Ok(value) => {
1554 workgroup_size_out[i] = value.0;
1555 }
1556 Err(err) => {
1557 if let Error::ConstantEvaluatorError(ref ty, _) = *err {
1558 match **ty {
1559 proc::ConstantEvaluatorError::OverrideExpr => {
1560 workgroup_size_overrides_out[i] =
1561 Some(self.workgroup_size_override(
1562 size_expr,
1563 &mut ctx.as_override(),
1564 )?);
1565 }
1566 _ => {
1567 return Err(err);
1568 }
1569 }
1570 } else {
1571 return Err(err);
1572 }
1573 }
1574 }
1575 }
1576 }
1577 if workgroup_size_overrides_out.iter().all(|x| x.is_none()) {
1578 (workgroup_size_out, None)
1579 } else {
1580 (workgroup_size_out, Some(workgroup_size_overrides_out))
1581 }
1582 } else {
1583 ([0; 3], None)
1584 };
1585
1586 let mesh_info = if let Some((var_name, var_span)) = entry.mesh_output_variable {
1587 let var = match ctx.globals.get(var_name) {
1588 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1589 Some(_) => {
1590 return Err(Box::new(Error::ExpectedGlobalVariable {
1591 name_span: var_span,
1592 }))
1593 }
1594 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1595 };
1596
1597 let mut info = ctx.module.analyze_mesh_shader_info(var);
1598 if let Some(h) = info.1[0] {
1599 info.0.max_vertices_override = Some(
1600 ctx.module
1601 .global_expressions
1602 .append(crate::Expression::Override(h), Span::UNDEFINED),
1603 );
1604 }
1605 if let Some(h) = info.1[1] {
1606 info.0.max_primitives_override = Some(
1607 ctx.module
1608 .global_expressions
1609 .append(crate::Expression::Override(h), Span::UNDEFINED),
1610 );
1611 }
1612
1613 Some(info.0)
1614 } else {
1615 None
1616 };
1617
1618 let task_payload = if let Some((var_name, var_span)) = entry.task_payload {
1619 Some(match ctx.globals.get(var_name) {
1620 Some(&LoweredGlobalDecl::Var(handle)) => handle,
1621 Some(_) => {
1622 return Err(Box::new(Error::ExpectedGlobalVariable {
1623 name_span: var_span,
1624 }))
1625 }
1626 None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1627 })
1628 } else {
1629 None
1630 };
1631
1632 ctx.module.entry_points.push(ir::EntryPoint {
1633 name: f.name.name.to_string(),
1634 stage: entry.stage,
1635 early_depth_test: entry.early_depth_test,
1636 workgroup_size,
1637 workgroup_size_overrides,
1638 function,
1639 mesh_info,
1640 task_payload,
1641 });
1642 Ok(LoweredGlobalDecl::EntryPoint(
1643 ctx.module.entry_points.len() - 1,
1644 ))
1645 } else {
1646 let handle = ctx.module.functions.append(function, span);
1647 Ok(LoweredGlobalDecl::Function {
1648 handle,
1649 must_use: f.result.as_ref().is_some_and(|res| res.must_use),
1650 })
1651 }
1652 }
1653
1654 fn workgroup_size_override(
1655 &mut self,
1656 size_expr: Handle<ast::Expression<'source>>,
1657 ctx: &mut ExpressionContext<'source, '_, '_>,
1658 ) -> Result<'source, Handle<ir::Expression>> {
1659 let span = ctx.ast_expressions.get_span(size_expr);
1660 let expr = self.expression(size_expr, ctx)?;
1661 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
1662 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr),
1663 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
1664 span,
1665 ))),
1666 }
1667 }
1668
1669 fn block(
1670 &mut self,
1671 b: &ast::Block<'source>,
1672 is_inside_loop: bool,
1673 ctx: &mut StatementContext<'source, '_, '_>,
1674 ) -> Result<'source, ir::Block> {
1675 let mut block = ir::Block::default();
1676
1677 for stmt in b.stmts.iter() {
1678 self.statement(stmt, &mut block, is_inside_loop, ctx)?;
1679 }
1680
1681 Ok(block)
1682 }
1683
1684 fn statement(
1685 &mut self,
1686 stmt: &ast::Statement<'source>,
1687 block: &mut ir::Block,
1688 is_inside_loop: bool,
1689 ctx: &mut StatementContext<'source, '_, '_>,
1690 ) -> Result<'source, ()> {
1691 let out = match stmt.kind {
1692 ast::StatementKind::Block(ref block) => {
1693 let block = self.block(block, is_inside_loop, ctx)?;
1694 ir::Statement::Block(block)
1695 }
1696 ast::StatementKind::LocalDecl(ref decl) => match *decl {
1697 ast::LocalDecl::Let(ref l) => {
1698 let mut emitter = proc::Emitter::default();
1699 emitter.start(&ctx.function.expressions);
1700
1701 let explicit_ty = l
1702 .ty
1703 .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter)))
1704 .transpose()?;
1705
1706 let mut ectx = ctx.as_expression(block, &mut emitter);
1707
1708 let (_ty, initializer) = self.type_and_init(
1709 l.name,
1710 Some(l.init),
1711 explicit_ty,
1712 AbstractRule::Concretize,
1713 &mut ectx,
1714 )?;
1715
1716 let initializer =
1719 initializer.expect("type_and_init did not return an initializer");
1720
1721 ctx.local_expression_kind_tracker
1727 .force_non_const(initializer);
1728
1729 block.extend(emitter.finish(&ctx.function.expressions));
1730 ctx.local_table
1731 .insert(l.handle, Declared::Runtime(Typed::Plain(initializer)));
1732 ctx.named_expressions
1733 .insert(initializer, (l.name.name.to_string(), l.name.span));
1734
1735 return Ok(());
1736 }
1737 ast::LocalDecl::Var(ref v) => {
1738 let mut emitter = proc::Emitter::default();
1739 emitter.start(&ctx.function.expressions);
1740
1741 let explicit_ty =
1742 v.ty.map(|ast| {
1743 self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter))
1744 })
1745 .transpose()?;
1746
1747 let mut ectx = ctx.as_expression(block, &mut emitter);
1748 let (ty, initializer) = self.type_and_init(
1749 v.name,
1750 v.init,
1751 explicit_ty,
1752 AbstractRule::Concretize,
1753 &mut ectx,
1754 )?;
1755
1756 let (const_initializer, initializer) = {
1757 match initializer {
1758 Some(init) => {
1759 if is_inside_loop
1767 || !ctx.local_expression_kind_tracker.is_const_or_override(init)
1768 {
1769 (None, Some(init))
1770 } else {
1771 (Some(init), None)
1772 }
1773 }
1774 None => (None, None),
1775 }
1776 };
1777
1778 let var = ctx.function.local_variables.append(
1779 ir::LocalVariable {
1780 name: Some(v.name.name.to_string()),
1781 ty,
1782 init: const_initializer,
1783 },
1784 stmt.span,
1785 );
1786
1787 let handle = ctx
1788 .as_expression(block, &mut emitter)
1789 .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?;
1790 block.extend(emitter.finish(&ctx.function.expressions));
1791 ctx.local_table
1792 .insert(v.handle, Declared::Runtime(Typed::Reference(handle)));
1793
1794 match initializer {
1795 Some(initializer) => ir::Statement::Store {
1796 pointer: handle,
1797 value: initializer,
1798 },
1799 None => return Ok(()),
1800 }
1801 }
1802 ast::LocalDecl::Const(ref c) => {
1803 let mut emitter = proc::Emitter::default();
1804 emitter.start(&ctx.function.expressions);
1805
1806 let ectx = &mut ctx.as_const(block, &mut emitter);
1807
1808 let explicit_ty =
1809 c.ty.map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const()))
1810 .transpose()?;
1811
1812 let (_ty, init) = self.type_and_init(
1813 c.name,
1814 Some(c.init),
1815 explicit_ty,
1816 AbstractRule::Allow,
1817 &mut ectx.as_const(),
1818 )?;
1819 let init = init.expect("Local const must have init");
1820
1821 block.extend(emitter.finish(&ctx.function.expressions));
1822 ctx.local_table
1823 .insert(c.handle, Declared::Const(Typed::Plain(init)));
1824 return Ok(());
1825 }
1826 },
1827 ast::StatementKind::If {
1828 condition,
1829 ref accept,
1830 ref reject,
1831 } => {
1832 let mut emitter = proc::Emitter::default();
1833 emitter.start(&ctx.function.expressions);
1834
1835 let condition =
1836 self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;
1837 block.extend(emitter.finish(&ctx.function.expressions));
1838
1839 let accept = self.block(accept, is_inside_loop, ctx)?;
1840 let reject = self.block(reject, is_inside_loop, ctx)?;
1841
1842 ir::Statement::If {
1843 condition,
1844 accept,
1845 reject,
1846 }
1847 }
1848 ast::StatementKind::Switch {
1849 selector,
1850 ref cases,
1851 } => {
1852 let mut emitter = proc::Emitter::default();
1853 emitter.start(&ctx.function.expressions);
1854
1855 let mut ectx = ctx.as_expression(block, &mut emitter);
1856
1857 let (mut exprs, spans) = core::iter::once(selector)
1860 .chain(cases.iter().filter_map(|case| match case.value {
1861 ast::SwitchValue::Expr(expr) => Some(expr),
1862 ast::SwitchValue::Default => None,
1863 }))
1864 .enumerate()
1865 .map(|(i, expr)| {
1866 let span = ectx.ast_expressions.get_span(expr);
1867 let expr = self.expression_for_abstract(expr, &mut ectx)?;
1868 let ty = resolve_inner!(ectx, expr);
1869 match *ty {
1870 ir::TypeInner::Scalar(
1871 ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT,
1872 ) => Ok((expr, span)),
1873 _ => match i {
1874 0 => Err(Box::new(Error::InvalidSwitchSelector { span })),
1875 _ => Err(Box::new(Error::InvalidSwitchCase { span })),
1876 },
1877 }
1878 })
1879 .collect::<Result<(Vec<_>, Vec<_>)>>()?;
1880
1881 let mut consensus =
1882 ectx.automatic_conversion_consensus(&exprs)
1883 .map_err(|span_idx| Error::SwitchCaseTypeMismatch {
1884 span: spans[span_idx],
1885 })?;
1886 if consensus == ir::Scalar::ABSTRACT_INT {
1888 consensus = ir::Scalar::I32;
1889 }
1890 for expr in &mut exprs {
1891 ectx.convert_to_leaf_scalar(expr, consensus)?;
1892 }
1893
1894 block.extend(emitter.finish(&ctx.function.expressions));
1895
1896 let mut exprs = exprs.into_iter();
1897 let selector = exprs
1898 .next()
1899 .expect("First element should be selector expression");
1900
1901 let cases = cases
1902 .iter()
1903 .map(|case| {
1904 Ok(ir::SwitchCase {
1905 value: match case.value {
1906 ast::SwitchValue::Expr(expr) => {
1907 let span = ctx.ast_expressions.get_span(expr);
1908 let expr = exprs.next().expect(
1909 "Should yield expression for each SwitchValue::Expr case",
1910 );
1911 match ctx
1912 .module
1913 .to_ctx()
1914 .eval_expr_to_literal_from(expr, &ctx.function.expressions)
1915 {
1916 Some(ir::Literal::I32(value)) => {
1917 ir::SwitchValue::I32(value)
1918 }
1919 Some(ir::Literal::U32(value)) => {
1920 ir::SwitchValue::U32(value)
1921 }
1922 _ => {
1923 return Err(Box::new(Error::InvalidSwitchCase {
1924 span,
1925 }));
1926 }
1927 }
1928 }
1929 ast::SwitchValue::Default => ir::SwitchValue::Default,
1930 },
1931 body: self.block(&case.body, is_inside_loop, ctx)?,
1932 fall_through: case.fall_through,
1933 })
1934 })
1935 .collect::<Result<_>>()?;
1936
1937 ir::Statement::Switch { selector, cases }
1938 }
1939 ast::StatementKind::Loop {
1940 ref body,
1941 ref continuing,
1942 break_if,
1943 } => {
1944 let body = self.block(body, true, ctx)?;
1945 let mut continuing = self.block(continuing, true, ctx)?;
1946
1947 let mut emitter = proc::Emitter::default();
1948 emitter.start(&ctx.function.expressions);
1949 let break_if = break_if
1950 .map(|expr| {
1951 self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))
1952 })
1953 .transpose()?;
1954 continuing.extend(emitter.finish(&ctx.function.expressions));
1955
1956 ir::Statement::Loop {
1957 body,
1958 continuing,
1959 break_if,
1960 }
1961 }
1962 ast::StatementKind::Break => ir::Statement::Break,
1963 ast::StatementKind::Continue => ir::Statement::Continue,
1964 ast::StatementKind::Return { value: ast_value } => {
1965 let mut emitter = proc::Emitter::default();
1966 emitter.start(&ctx.function.expressions);
1967
1968 let value;
1969 if let Some(ast_expr) = ast_value {
1970 let result_ty = ctx.function.result.as_ref().map(|r| r.ty);
1971 let mut ectx = ctx.as_expression(block, &mut emitter);
1972 let expr = self.expression_for_abstract(ast_expr, &mut ectx)?;
1973
1974 if let Some(result_ty) = result_ty {
1975 let mut ectx = ctx.as_expression(block, &mut emitter);
1976 let resolution = proc::TypeResolution::Handle(result_ty);
1977 let converted =
1978 ectx.try_automatic_conversions(expr, &resolution, Span::default())?;
1979 value = Some(converted);
1980 } else {
1981 value = Some(expr);
1982 }
1983 } else {
1984 value = None;
1985 }
1986 block.extend(emitter.finish(&ctx.function.expressions));
1987
1988 ir::Statement::Return { value }
1989 }
1990 ast::StatementKind::Kill => ir::Statement::Kill,
1991 ast::StatementKind::Call {
1992 ref function,
1993 ref arguments,
1994 } => {
1995 let mut emitter = proc::Emitter::default();
1996 emitter.start(&ctx.function.expressions);
1997
1998 let _ = self.call(
1999 stmt.span,
2000 function,
2001 arguments,
2002 &mut ctx.as_expression(block, &mut emitter),
2003 true,
2004 )?;
2005 block.extend(emitter.finish(&ctx.function.expressions));
2006 return Ok(());
2007 }
2008 ast::StatementKind::Assign {
2009 target: ast_target,
2010 op,
2011 value,
2012 } => {
2013 let mut emitter = proc::Emitter::default();
2014 emitter.start(&ctx.function.expressions);
2015 let target_span = ctx.ast_expressions.get_span(ast_target);
2016
2017 let mut ectx = ctx.as_expression(block, &mut emitter);
2018 let target = self.expression_for_reference(ast_target, &mut ectx)?;
2019 let target_handle = match target {
2020 Typed::Reference(handle) => handle,
2021 Typed::Plain(handle) => {
2022 let ty = ctx.invalid_assignment_type(handle);
2023 return Err(Box::new(Error::InvalidAssignment {
2024 span: target_span,
2025 ty,
2026 }));
2027 }
2028 };
2029
2030 let target_scalar = match op {
2035 Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => {
2036 Some(ir::Scalar::U32)
2037 }
2038 _ => resolve_inner!(ectx, target_handle)
2039 .pointer_automatically_convertible_scalar(&ectx.module.types),
2040 };
2041
2042 let value = self.expression_for_abstract(value, &mut ectx)?;
2043 let mut value = match target_scalar {
2044 Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar(
2045 value,
2046 target_scalar,
2047 target_span,
2048 )?,
2049 None => value,
2050 };
2051
2052 let value = match op {
2053 Some(op) => {
2054 let mut left = ectx.apply_load_rule(target)?;
2055 ectx.binary_op_splat(op, &mut left, &mut value)?;
2056 ectx.append_expression(
2057 ir::Expression::Binary {
2058 op,
2059 left,
2060 right: value,
2061 },
2062 stmt.span,
2063 )?
2064 }
2065 None => value,
2066 };
2067 block.extend(emitter.finish(&ctx.function.expressions));
2068
2069 ir::Statement::Store {
2070 pointer: target_handle,
2071 value,
2072 }
2073 }
2074 ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {
2075 let mut emitter = proc::Emitter::default();
2076 emitter.start(&ctx.function.expressions);
2077
2078 let op = match stmt.kind {
2079 ast::StatementKind::Increment(_) => ir::BinaryOperator::Add,
2080 ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract,
2081 _ => unreachable!(),
2082 };
2083
2084 let value_span = ctx.ast_expressions.get_span(value);
2085 let target = self
2086 .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;
2087 let target_handle = match target {
2088 Typed::Reference(handle) => handle,
2089 Typed::Plain(_) => {
2090 return Err(Box::new(Error::BadIncrDecrReferenceType(value_span)))
2091 }
2092 };
2093
2094 let mut ectx = ctx.as_expression(block, &mut emitter);
2095 let scalar = match *resolve_inner!(ectx, target_handle) {
2096 ir::TypeInner::ValuePointer {
2097 size: None, scalar, ..
2098 } => scalar,
2099 ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {
2100 ir::TypeInner::Scalar(scalar) => scalar,
2101 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2102 },
2103 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2104 };
2105 let literal = match scalar.kind {
2106 ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar)
2107 .ok_or(Error::BadIncrDecrReferenceType(value_span))?,
2108 _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2109 };
2110
2111 let right =
2112 ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?;
2113 let rctx = ectx.runtime_expression_ctx(stmt.span)?;
2114 let left = rctx.function.expressions.append(
2115 ir::Expression::Load {
2116 pointer: target_handle,
2117 },
2118 value_span,
2119 );
2120 let value = rctx
2121 .function
2122 .expressions
2123 .append(ir::Expression::Binary { op, left, right }, stmt.span);
2124 rctx.local_expression_kind_tracker
2125 .insert(left, proc::ExpressionKind::Runtime);
2126 rctx.local_expression_kind_tracker
2127 .insert(value, proc::ExpressionKind::Runtime);
2128
2129 block.extend(emitter.finish(&ctx.function.expressions));
2130 ir::Statement::Store {
2131 pointer: target_handle,
2132 value,
2133 }
2134 }
2135 ast::StatementKind::ConstAssert(condition) => {
2136 let mut emitter = proc::Emitter::default();
2137 emitter.start(&ctx.function.expressions);
2138
2139 let condition =
2140 self.expression(condition, &mut ctx.as_const(block, &mut emitter))?;
2141
2142 let span = ctx.function.expressions.get_span(condition);
2143 match ctx
2144 .module
2145 .to_ctx()
2146 .eval_expr_to_bool_from(condition, &ctx.function.expressions)
2147 {
2148 Some(true) => Ok(()),
2149 Some(false) => Err(Error::ConstAssertFailed(span)),
2150 _ => Err(Error::NotBool(span)),
2151 }?;
2152
2153 block.extend(emitter.finish(&ctx.function.expressions));
2154
2155 return Ok(());
2156 }
2157 ast::StatementKind::Phony(expr) => {
2158 let mut emitter = proc::Emitter::default();
2162 emitter.start(&ctx.function.expressions);
2163
2164 let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;
2165 block.extend(emitter.finish(&ctx.function.expressions));
2166 ctx.named_expressions
2167 .insert(value, ("phony".to_string(), stmt.span));
2168 return Ok(());
2169 }
2170 };
2171
2172 block.push(out, stmt.span);
2173
2174 Ok(())
2175 }
2176
2177 fn expression(
2185 &mut self,
2186 expr: Handle<ast::Expression<'source>>,
2187 ctx: &mut ExpressionContext<'source, '_, '_>,
2188 ) -> Result<'source, Handle<ir::Expression>> {
2189 let expr = self.expression_for_abstract(expr, ctx)?;
2190 ctx.concretize(expr)
2191 }
2192
2193 fn expression_for_abstract(
2194 &mut self,
2195 expr: Handle<ast::Expression<'source>>,
2196 ctx: &mut ExpressionContext<'source, '_, '_>,
2197 ) -> Result<'source, Handle<ir::Expression>> {
2198 let expr = self.expression_for_reference(expr, ctx)?;
2199 ctx.apply_load_rule(expr)
2200 }
2201
2202 fn expression_with_leaf_scalar(
2203 &mut self,
2204 expr: Handle<ast::Expression<'source>>,
2205 scalar: ir::Scalar,
2206 ctx: &mut ExpressionContext<'source, '_, '_>,
2207 ) -> Result<'source, Handle<ir::Expression>> {
2208 let unconverted = self.expression_for_abstract(expr, ctx)?;
2209 ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default())
2210 }
2211
2212 fn expression_for_reference(
2213 &mut self,
2214 expr: Handle<ast::Expression<'source>>,
2215 ctx: &mut ExpressionContext<'source, '_, '_>,
2216 ) -> Result<'source, Typed<Handle<ir::Expression>>> {
2217 let span = ctx.ast_expressions.get_span(expr);
2218 let expr = &ctx.ast_expressions[expr];
2219
2220 let expr: Typed<ir::Expression> = match *expr {
2221 ast::Expression::Literal(literal) => {
2222 let literal = match literal {
2223 ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f),
2224 ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f),
2225 ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i),
2226 ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u),
2227 ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i),
2228 ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u),
2229 ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f),
2230 ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i),
2231 ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f),
2232 ast::Literal::Bool(b) => ir::Literal::Bool(b),
2233 };
2234 let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?;
2235 return Ok(Typed::Plain(handle));
2236 }
2237 ast::Expression::Ident(ast::IdentExpr::Local(local)) => {
2238 return ctx.local(&local, span);
2239 }
2240 ast::Expression::Ident(ast::IdentExpr::Unresolved(name)) => {
2241 let global = ctx
2242 .globals
2243 .get(name)
2244 .ok_or(Error::UnknownIdent(span, name))?;
2245 let expr = match *global {
2246 LoweredGlobalDecl::Var(handle) => {
2247 let expr = ir::Expression::GlobalVariable(handle);
2248 match ctx.module.global_variables[handle].space {
2249 ir::AddressSpace::Handle => Typed::Plain(expr),
2250 _ => Typed::Reference(expr),
2251 }
2252 }
2253 LoweredGlobalDecl::Const(handle) => {
2254 Typed::Plain(ir::Expression::Constant(handle))
2255 }
2256 LoweredGlobalDecl::Override(handle) => {
2257 Typed::Plain(ir::Expression::Override(handle))
2258 }
2259 LoweredGlobalDecl::Function { .. }
2260 | LoweredGlobalDecl::Type(_)
2261 | LoweredGlobalDecl::EntryPoint(_) => {
2262 return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable)));
2263 }
2264 };
2265
2266 return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));
2267 }
2268 ast::Expression::Construct {
2269 ref ty,
2270 ty_span,
2271 ref components,
2272 } => {
2273 let handle = self.construct(span, ty, ty_span, components, ctx)?;
2274 return Ok(Typed::Plain(handle));
2275 }
2276 ast::Expression::Unary { op, expr } => {
2277 let expr = self.expression_for_abstract(expr, ctx)?;
2278 Typed::Plain(ir::Expression::Unary { op, expr })
2279 }
2280 ast::Expression::AddrOf(expr) => {
2281 match self.expression_for_reference(expr, ctx)? {
2284 Typed::Reference(handle) => {
2285 let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle];
2286 if let &ir::Expression::Access { base, .. }
2287 | &ir::Expression::AccessIndex { base, .. } = expr
2288 {
2289 if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() {
2290 if matches!(
2291 *ty.inner_with(&ctx.module.types),
2292 ir::TypeInner::Vector { .. },
2293 ) {
2294 return Err(Box::new(Error::InvalidAddrOfOperand(
2295 ctx.get_expression_span(handle),
2296 )));
2297 }
2298 }
2299 }
2300 return Ok(Typed::Plain(handle));
2302 }
2303 Typed::Plain(_) => {
2304 return Err(Box::new(Error::NotReference(
2305 "the operand of the `&` operator",
2306 span,
2307 )));
2308 }
2309 }
2310 }
2311 ast::Expression::Deref(expr) => {
2312 let pointer = self.expression(expr, ctx)?;
2314
2315 if resolve_inner!(ctx, pointer).pointer_space().is_none() {
2316 return Err(Box::new(Error::NotPointer(span)));
2317 }
2318
2319 return Ok(Typed::Reference(pointer));
2321 }
2322 ast::Expression::Binary { op, left, right } => {
2323 self.binary(op, left, right, span, ctx)?
2324 }
2325 ast::Expression::Call {
2326 ref function,
2327 ref arguments,
2328 } => {
2329 let handle = self
2330 .call(span, function, arguments, ctx, false)?
2331 .ok_or(Error::FunctionReturnsVoid(function.span))?;
2332 return Ok(Typed::Plain(handle));
2333 }
2334 ast::Expression::Index { base, index } => {
2335 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2336 let index = self.expression(index, ctx)?;
2337
2338 if let Typed::Plain(handle) = lowered_base {
2341 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2342 lowered_base = Typed::Reference(handle);
2343 }
2344 }
2345
2346 lowered_base.try_map(|base| match ctx.const_eval_expr_to_u32(index).ok() {
2347 Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }),
2348 None => {
2349 let base = ctx.concretize(base)?;
2355 Ok(ir::Expression::Access { base, index })
2356 }
2357 })?
2358 }
2359 ast::Expression::Member { base, ref field } => {
2360 let mut lowered_base = self.expression_for_reference(base, ctx)?;
2361
2362 if let Typed::Plain(handle) = lowered_base {
2365 if resolve_inner!(ctx, handle).pointer_space().is_some() {
2366 lowered_base = Typed::Reference(handle);
2367 }
2368 }
2369
2370 let temp_ty;
2371 let composite_type: &ir::TypeInner = match lowered_base {
2372 Typed::Reference(handle) => {
2373 temp_ty = resolve_inner!(ctx, handle)
2374 .pointer_base_type()
2375 .expect("In Typed::Reference(handle), handle must be a Naga pointer");
2376 temp_ty.inner_with(&ctx.module.types)
2377 }
2378
2379 Typed::Plain(handle) => {
2380 resolve_inner!(ctx, handle)
2381 }
2382 };
2383
2384 let access = match *composite_type {
2385 ir::TypeInner::Struct { ref members, .. } => {
2386 let index = members
2387 .iter()
2388 .position(|m| m.name.as_deref() == Some(field.name))
2389 .ok_or(Error::BadAccessor(field.span))?
2390 as u32;
2391
2392 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2393 }
2394 ir::TypeInner::Vector { .. } => {
2395 match Components::new(field.name, field.span)? {
2396 Components::Swizzle { size, pattern } => {
2397 Typed::Plain(ir::Expression::Swizzle {
2398 size,
2399 vector: ctx.apply_load_rule(lowered_base)?,
2400 pattern,
2401 })
2402 }
2403 Components::Single(index) => {
2404 lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2405 }
2406 }
2407 }
2408 _ => return Err(Box::new(Error::BadAccessor(field.span))),
2409 };
2410
2411 access
2412 }
2413 ast::Expression::Bitcast { expr, to, ty_span } => {
2414 let expr = self.expression(expr, ctx)?;
2415 let to_resolved = self.resolve_ast_type(to, &mut ctx.as_const())?;
2416
2417 let element_scalar = match ctx.module.types[to_resolved].inner {
2418 ir::TypeInner::Scalar(scalar) => scalar,
2419 ir::TypeInner::Vector { scalar, .. } => scalar,
2420 _ => {
2421 let ty = resolve!(ctx, expr);
2422 return Err(Box::new(Error::BadTypeCast {
2423 from_type: ctx.type_resolution_to_string(ty),
2424 span: ty_span,
2425 to_type: ctx.type_to_string(to_resolved),
2426 }));
2427 }
2428 };
2429
2430 Typed::Plain(ir::Expression::As {
2431 expr,
2432 kind: element_scalar.kind,
2433 convert: None,
2434 })
2435 }
2436 };
2437
2438 expr.try_map(|handle| ctx.append_expression(handle, span))
2439 }
2440
2441 fn logical(
2445 &mut self,
2446 op: crate::BinaryOperator,
2447 left: Handle<crate::Expression>,
2448 right: Handle<ast::Expression<'source>>,
2449 span: Span,
2450 ctx: &mut ExpressionContext<'source, '_, '_>,
2451 ) -> Result<'source, Typed<crate::Expression>> {
2452 debug_assert!(
2453 op == crate::BinaryOperator::LogicalAnd || op == crate::BinaryOperator::LogicalOr
2454 );
2455
2456 if ctx.is_runtime() {
2457 let (condition, else_val) = if op == crate::BinaryOperator::LogicalAnd {
2469 let condition = left;
2470 let else_val = ctx.append_expression(
2471 crate::Expression::Literal(crate::Literal::Bool(false)),
2472 span,
2473 )?;
2474 (condition, else_val)
2475 } else {
2476 let condition = ctx.append_expression(
2477 crate::Expression::Unary {
2478 op: crate::UnaryOperator::LogicalNot,
2479 expr: left,
2480 },
2481 span,
2482 )?;
2483 let else_val = ctx.append_expression(
2484 crate::Expression::Literal(crate::Literal::Bool(true)),
2485 span,
2486 )?;
2487 (condition, else_val)
2488 };
2489
2490 let bool_ty = ctx.ensure_type_exists(crate::TypeInner::Scalar(crate::Scalar::BOOL));
2491
2492 let rctx = ctx.runtime_expression_ctx(span)?;
2493 let result_var = rctx.function.local_variables.append(
2494 crate::LocalVariable {
2495 name: None,
2496 ty: bool_ty,
2497 init: None,
2498 },
2499 span,
2500 );
2501 let pointer =
2502 ctx.append_expression(crate::Expression::LocalVariable(result_var), span)?;
2503
2504 let (right, mut accept) = ctx.with_nested_runtime_expression_ctx(span, |ctx| {
2505 let right = self.expression_for_abstract(right, ctx)?;
2506 ctx.grow_types(right)?;
2507 Ok(right)
2508 })?;
2509
2510 accept.push(
2511 crate::Statement::Store {
2512 pointer,
2513 value: right,
2514 },
2515 span,
2516 );
2517
2518 let mut reject = crate::Block::with_capacity(1);
2519 reject.push(
2520 crate::Statement::Store {
2521 pointer,
2522 value: else_val,
2523 },
2524 span,
2525 );
2526
2527 let rctx = ctx.runtime_expression_ctx(span)?;
2528 rctx.block.push(
2529 crate::Statement::If {
2530 condition,
2531 accept,
2532 reject,
2533 },
2534 span,
2535 );
2536
2537 Ok(Typed::Reference(crate::Expression::LocalVariable(
2538 result_var,
2539 )))
2540 } else {
2541 let left_expr = ctx.get(left);
2542 let &crate::Expression::Literal(crate::Literal::Bool(left_val)) = left_expr else {
2544 return Err(Box::new(Error::NotBool(span)));
2545 };
2546
2547 if op == crate::BinaryOperator::LogicalAnd && !left_val
2548 || op == crate::BinaryOperator::LogicalOr && left_val
2549 {
2550 Ok(Typed::Plain(left_expr.clone()))
2556 } else {
2557 let right = self.expression_for_abstract(right, ctx)?;
2558 ctx.grow_types(right)?;
2559
2560 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2561 }
2562 }
2563 }
2564
2565 fn binary(
2566 &mut self,
2567 op: ir::BinaryOperator,
2568 left: Handle<ast::Expression<'source>>,
2569 right: Handle<ast::Expression<'source>>,
2570 span: Span,
2571 ctx: &mut ExpressionContext<'source, '_, '_>,
2572 ) -> Result<'source, Typed<ir::Expression>> {
2573 if op == ir::BinaryOperator::LogicalAnd || op == ir::BinaryOperator::LogicalOr {
2574 let left = self.expression_for_abstract(left, ctx)?;
2575 ctx.grow_types(left)?;
2576
2577 if !matches!(
2578 resolve_inner!(ctx, left),
2579 &ir::TypeInner::Scalar(ir::Scalar::BOOL)
2580 ) {
2581 let right = self.expression_for_abstract(right, ctx)?;
2583 ctx.grow_types(right)?;
2584 Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2585 } else {
2586 self.logical(op, left, right, span, ctx)
2587 }
2588 } else {
2589 let mut left = self.expression_for_abstract(left, ctx)?;
2591 let mut right = self.expression_for_abstract(right, ctx)?;
2592
2593 ctx.binary_op_splat(op, &mut left, &mut right)?;
2596
2597 match op {
2599 ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => {
2600 right =
2605 ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?;
2606
2607 if !ctx.is_const(right) {
2619 left = ctx.concretize(left)?;
2620 }
2621 }
2622
2623 _ => {
2628 ctx.grow_types(left)?;
2629 ctx.grow_types(right)?;
2630 if let Ok(consensus_scalar) =
2631 ctx.automatic_conversion_consensus([left, right].iter())
2632 {
2633 ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;
2634 ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;
2635 }
2636 }
2637 }
2638
2639 Ok(Typed::Plain(ir::Expression::Binary { op, left, right }))
2640 }
2641 }
2642
2643 fn call(
2662 &mut self,
2663 span: Span,
2664 function: &ast::Ident<'source>,
2665 arguments: &[Handle<ast::Expression<'source>>],
2666 ctx: &mut ExpressionContext<'source, '_, '_>,
2667 is_statement: bool,
2668 ) -> Result<'source, Option<Handle<ir::Expression>>> {
2669 let function_span = function.span;
2670 match ctx.globals.get(function.name) {
2671 Some(&LoweredGlobalDecl::Type(ty)) => {
2672 let handle = self.construct(
2673 span,
2674 &ast::ConstructorType::Type(ty),
2675 function_span,
2676 arguments,
2677 ctx,
2678 )?;
2679 Ok(Some(handle))
2680 }
2681 Some(
2682 &LoweredGlobalDecl::Const(_)
2683 | &LoweredGlobalDecl::Override(_)
2684 | &LoweredGlobalDecl::Var(_),
2685 ) => Err(Box::new(Error::Unexpected(
2686 function_span,
2687 ExpectedToken::Function,
2688 ))),
2689 Some(&LoweredGlobalDecl::EntryPoint(_)) => {
2690 Err(Box::new(Error::CalledEntryPoint(function_span)))
2691 }
2692 Some(&LoweredGlobalDecl::Function {
2693 handle: function,
2694 must_use,
2695 }) => {
2696 let arguments = arguments
2697 .iter()
2698 .enumerate()
2699 .map(|(i, &arg)| {
2700 let Some(&ir::FunctionArgument {
2702 ty: parameter_ty, ..
2703 }) = ctx.module.functions[function].arguments.get(i)
2704 else {
2705 return self.expression(arg, ctx);
2708 };
2709
2710 let expr = self.expression_for_abstract(arg, ctx)?;
2711 ctx.try_automatic_conversions(
2712 expr,
2713 &proc::TypeResolution::Handle(parameter_ty),
2714 ctx.ast_expressions.get_span(arg),
2715 )
2716 })
2717 .collect::<Result<Vec<_>>>()?;
2718
2719 let has_result = ctx.module.functions[function].result.is_some();
2720
2721 if must_use && is_statement {
2722 return Err(Box::new(Error::FunctionMustUseUnused(function_span)));
2723 }
2724
2725 let rctx = ctx.runtime_expression_ctx(span)?;
2726 rctx.block
2728 .extend(rctx.emitter.finish(&rctx.function.expressions));
2729 let result = has_result.then(|| {
2730 let result = rctx
2731 .function
2732 .expressions
2733 .append(ir::Expression::CallResult(function), span);
2734 rctx.local_expression_kind_tracker
2735 .insert(result, proc::ExpressionKind::Runtime);
2736 result
2737 });
2738 rctx.emitter.start(&rctx.function.expressions);
2739 rctx.block.push(
2740 ir::Statement::Call {
2741 function,
2742 arguments,
2743 result,
2744 },
2745 span,
2746 );
2747
2748 Ok(result)
2749 }
2750 None => {
2751 let span = function_span;
2752 let expr = if let Some(fun) = conv::map_relational_fun(function.name) {
2753 let mut args = ctx.prepare_args(arguments, 1, span);
2754 let argument = self.expression(args.next()?, ctx)?;
2755 args.finish()?;
2756
2757 let argument_unmodified = matches!(
2759 fun,
2760 ir::RelationalFunction::All | ir::RelationalFunction::Any
2761 ) && {
2762 matches!(
2763 resolve_inner!(ctx, argument),
2764 &ir::TypeInner::Scalar(ir::Scalar {
2765 kind: ir::ScalarKind::Bool,
2766 ..
2767 })
2768 )
2769 };
2770
2771 if argument_unmodified {
2772 return Ok(Some(argument));
2773 } else {
2774 ir::Expression::Relational { fun, argument }
2775 }
2776 } else if let Some((axis, ctrl)) = conv::map_derivative(function.name) {
2777 let mut args = ctx.prepare_args(arguments, 1, span);
2778 let expr = self.expression(args.next()?, ctx)?;
2779 args.finish()?;
2780
2781 ir::Expression::Derivative { axis, ctrl, expr }
2782 } else if let Some(fun) = conv::map_standard_fun(function.name) {
2783 self.math_function_helper(span, fun, arguments, ctx)?
2784 } else if let Some(fun) = Texture::map(function.name) {
2785 self.texture_sample_helper(fun, arguments, span, ctx)?
2786 } else if let Some((op, cop)) = conv::map_subgroup_operation(function.name) {
2787 return Ok(Some(
2788 self.subgroup_operation_helper(span, op, cop, arguments, ctx)?,
2789 ));
2790 } else if let Some(mode) = SubgroupGather::map(function.name) {
2791 return Ok(Some(
2792 self.subgroup_gather_helper(span, mode, arguments, ctx)?,
2793 ));
2794 } else if let Some(fun) = ir::AtomicFunction::map(function.name) {
2795 return self.atomic_helper(span, fun, arguments, is_statement, ctx);
2796 } else {
2797 match function.name {
2798 "select" => {
2799 let mut args = ctx.prepare_args(arguments, 3, span);
2800
2801 let reject_orig = args.next()?;
2802 let accept_orig = args.next()?;
2803 let mut values = [
2804 self.expression_for_abstract(reject_orig, ctx)?,
2805 self.expression_for_abstract(accept_orig, ctx)?,
2806 ];
2807 let condition = self.expression(args.next()?, ctx)?;
2808
2809 args.finish()?;
2810
2811 let diagnostic_details =
2812 |ctx: &ExpressionContext<'_, '_, '_>,
2813 ty_res: &proc::TypeResolution,
2814 orig_expr| {
2815 (
2816 ctx.ast_expressions.get_span(orig_expr),
2817 format!("`{}`", ctx.as_diagnostic_display(ty_res)),
2818 )
2819 };
2820 for (&value, orig_value) in
2821 values.iter().zip([reject_orig, accept_orig])
2822 {
2823 let value_ty_res = resolve!(ctx, value);
2824 if value_ty_res
2825 .inner_with(&ctx.module.types)
2826 .vector_size_and_scalar()
2827 .is_none()
2828 {
2829 let (arg_span, arg_type) =
2830 diagnostic_details(ctx, value_ty_res, orig_value);
2831 return Err(Box::new(Error::SelectUnexpectedArgumentType {
2832 arg_span,
2833 arg_type,
2834 }));
2835 }
2836 }
2837 let mut consensus_scalar = ctx
2838 .automatic_conversion_consensus(&values)
2839 .map_err(|_idx| {
2840 let [reject, accept] = values;
2841 let [(reject_span, reject_type), (accept_span, accept_type)] =
2842 [(reject_orig, reject), (accept_orig, accept)].map(
2843 |(orig_expr, expr)| {
2844 let ty_res = &ctx.typifier()[expr];
2845 diagnostic_details(ctx, ty_res, orig_expr)
2846 },
2847 );
2848 Error::SelectRejectAndAcceptHaveNoCommonType {
2849 reject_span,
2850 reject_type,
2851 accept_span,
2852 accept_type,
2853 }
2854 })?;
2855 if !ctx.is_const(condition) {
2856 consensus_scalar = consensus_scalar.concretize();
2857 }
2858
2859 ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;
2860
2861 let [reject, accept] = values;
2862
2863 ir::Expression::Select {
2864 reject,
2865 accept,
2866 condition,
2867 }
2868 }
2869 "arrayLength" => {
2870 let mut args = ctx.prepare_args(arguments, 1, span);
2871 let expr = self.expression(args.next()?, ctx)?;
2872 args.finish()?;
2873
2874 ir::Expression::ArrayLength(expr)
2875 }
2876 "atomicLoad" => {
2877 let mut args = ctx.prepare_args(arguments, 1, span);
2878 let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?;
2879 args.finish()?;
2880
2881 ir::Expression::Load { pointer }
2882 }
2883 "atomicStore" => {
2884 let mut args = ctx.prepare_args(arguments, 2, span);
2885 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
2886 let value =
2887 self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
2888 args.finish()?;
2889
2890 let rctx = ctx.runtime_expression_ctx(span)?;
2891 rctx.block
2892 .extend(rctx.emitter.finish(&rctx.function.expressions));
2893 rctx.emitter.start(&rctx.function.expressions);
2894 rctx.block
2895 .push(ir::Statement::Store { pointer, value }, span);
2896 return Ok(None);
2897 }
2898 "atomicCompareExchangeWeak" => {
2899 let mut args = ctx.prepare_args(arguments, 3, span);
2900
2901 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
2902
2903 let compare =
2904 self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
2905
2906 let value = args.next()?;
2907 let value_span = ctx.ast_expressions.get_span(value);
2908 let value = self.expression_with_leaf_scalar(value, scalar, ctx)?;
2909
2910 args.finish()?;
2911
2912 let expression = match *resolve_inner!(ctx, value) {
2913 ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult {
2914 ty: ctx.module.generate_predeclared_type(
2915 ir::PredeclaredType::AtomicCompareExchangeWeakResult(
2916 scalar,
2917 ),
2918 ),
2919 comparison: true,
2920 },
2921 _ => {
2922 return Err(Box::new(Error::InvalidAtomicOperandType(
2923 value_span,
2924 )))
2925 }
2926 };
2927
2928 let result = ctx.interrupt_emitter(expression, span)?;
2929 let rctx = ctx.runtime_expression_ctx(span)?;
2930 rctx.block.push(
2931 ir::Statement::Atomic {
2932 pointer,
2933 fun: ir::AtomicFunction::Exchange {
2934 compare: Some(compare),
2935 },
2936 value,
2937 result: Some(result),
2938 },
2939 span,
2940 );
2941 return Ok(Some(result));
2942 }
2943 "textureAtomicMin" | "textureAtomicMax" | "textureAtomicAdd"
2944 | "textureAtomicAnd" | "textureAtomicOr" | "textureAtomicXor" => {
2945 let mut args = ctx.prepare_args(arguments, 3, span);
2946
2947 let image = args.next()?;
2948 let image_span = ctx.ast_expressions.get_span(image);
2949 let image = self.expression(image, ctx)?;
2950
2951 let coordinate = self.expression(args.next()?, ctx)?;
2952
2953 let (_, arrayed) = ctx.image_data(image, image_span)?;
2954 let array_index = arrayed
2955 .then(|| {
2956 args.min_args += 1;
2957 self.expression(args.next()?, ctx)
2958 })
2959 .transpose()?;
2960
2961 let value = self.expression(args.next()?, ctx)?;
2962
2963 args.finish()?;
2964
2965 let rctx = ctx.runtime_expression_ctx(span)?;
2966 rctx.block
2967 .extend(rctx.emitter.finish(&rctx.function.expressions));
2968 rctx.emitter.start(&rctx.function.expressions);
2969 let stmt = ir::Statement::ImageAtomic {
2970 image,
2971 coordinate,
2972 array_index,
2973 fun: match function.name {
2974 "textureAtomicMin" => ir::AtomicFunction::Min,
2975 "textureAtomicMax" => ir::AtomicFunction::Max,
2976 "textureAtomicAdd" => ir::AtomicFunction::Add,
2977 "textureAtomicAnd" => ir::AtomicFunction::And,
2978 "textureAtomicOr" => ir::AtomicFunction::InclusiveOr,
2979 "textureAtomicXor" => ir::AtomicFunction::ExclusiveOr,
2980 _ => unreachable!(),
2981 },
2982 value,
2983 };
2984 rctx.block.push(stmt, span);
2985 return Ok(None);
2986 }
2987 "storageBarrier" => {
2988 ctx.prepare_args(arguments, 0, span).finish()?;
2989
2990 let rctx = ctx.runtime_expression_ctx(span)?;
2991 rctx.block
2992 .push(ir::Statement::ControlBarrier(ir::Barrier::STORAGE), span);
2993 return Ok(None);
2994 }
2995 "workgroupBarrier" => {
2996 ctx.prepare_args(arguments, 0, span).finish()?;
2997
2998 let rctx = ctx.runtime_expression_ctx(span)?;
2999 rctx.block
3000 .push(ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP), span);
3001 return Ok(None);
3002 }
3003 "subgroupBarrier" => {
3004 ctx.prepare_args(arguments, 0, span).finish()?;
3005
3006 let rctx = ctx.runtime_expression_ctx(span)?;
3007 rctx.block
3008 .push(ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP), span);
3009 return Ok(None);
3010 }
3011 "textureBarrier" => {
3012 ctx.prepare_args(arguments, 0, span).finish()?;
3013
3014 let rctx = ctx.runtime_expression_ctx(span)?;
3015 rctx.block
3016 .push(ir::Statement::ControlBarrier(ir::Barrier::TEXTURE), span);
3017 return Ok(None);
3018 }
3019 "workgroupUniformLoad" => {
3020 let mut args = ctx.prepare_args(arguments, 1, span);
3021 let expr = args.next()?;
3022 args.finish()?;
3023
3024 let pointer = self.expression(expr, ctx)?;
3025 let result_ty = match *resolve_inner!(ctx, pointer) {
3026 ir::TypeInner::Pointer {
3027 base,
3028 space: ir::AddressSpace::WorkGroup,
3029 } => base,
3030 ref other => {
3031 log::error!("Type {other:?} passed to workgroupUniformLoad");
3032 let span = ctx.ast_expressions.get_span(expr);
3033 return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span)));
3034 }
3035 };
3036 let result = ctx.interrupt_emitter(
3037 ir::Expression::WorkGroupUniformLoadResult { ty: result_ty },
3038 span,
3039 )?;
3040 let rctx = ctx.runtime_expression_ctx(span)?;
3041 rctx.block.push(
3042 ir::Statement::WorkGroupUniformLoad { pointer, result },
3043 span,
3044 );
3045
3046 return Ok(Some(result));
3047 }
3048 "textureStore" => {
3049 let mut args = ctx.prepare_args(arguments, 3, span);
3050
3051 let image = args.next()?;
3052 let image_span = ctx.ast_expressions.get_span(image);
3053 let image = self.expression(image, ctx)?;
3054
3055 let coordinate = self.expression(args.next()?, ctx)?;
3056
3057 let (class, arrayed) = ctx.image_data(image, image_span)?;
3058 let array_index = arrayed
3059 .then(|| {
3060 args.min_args += 1;
3061 self.expression(args.next()?, ctx)
3062 })
3063 .transpose()?;
3064 let scalar = if let ir::ImageClass::Storage { format, .. } = class {
3065 format.into()
3066 } else {
3067 return Err(Box::new(Error::NotStorageTexture(image_span)));
3068 };
3069
3070 let value =
3071 self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3072
3073 args.finish()?;
3074
3075 let rctx = ctx.runtime_expression_ctx(span)?;
3076 rctx.block
3077 .extend(rctx.emitter.finish(&rctx.function.expressions));
3078 rctx.emitter.start(&rctx.function.expressions);
3079 let stmt = ir::Statement::ImageStore {
3080 image,
3081 coordinate,
3082 array_index,
3083 value,
3084 };
3085 rctx.block.push(stmt, span);
3086 return Ok(None);
3087 }
3088 "textureLoad" => {
3089 let mut args = ctx.prepare_args(arguments, 2, span);
3090
3091 let image = args.next()?;
3092 let image_span = ctx.ast_expressions.get_span(image);
3093 let image = self.expression(image, ctx)?;
3094
3095 let coordinate = self.expression(args.next()?, ctx)?;
3096
3097 let (class, arrayed) = ctx.image_data(image, image_span)?;
3098 let array_index = arrayed
3099 .then(|| {
3100 args.min_args += 1;
3101 self.expression(args.next()?, ctx)
3102 })
3103 .transpose()?;
3104
3105 let level = class
3106 .is_mipmapped()
3107 .then(|| {
3108 args.min_args += 1;
3109 self.expression(args.next()?, ctx)
3110 })
3111 .transpose()?;
3112
3113 let sample = class
3114 .is_multisampled()
3115 .then(|| self.expression(args.next()?, ctx))
3116 .transpose()?;
3117
3118 args.finish()?;
3119
3120 ir::Expression::ImageLoad {
3121 image,
3122 coordinate,
3123 array_index,
3124 level,
3125 sample,
3126 }
3127 }
3128 "textureDimensions" => {
3129 let mut args = ctx.prepare_args(arguments, 1, span);
3130 let image = self.expression(args.next()?, ctx)?;
3131 let level = args
3132 .next()
3133 .map(|arg| self.expression(arg, ctx))
3134 .ok()
3135 .transpose()?;
3136 args.finish()?;
3137
3138 ir::Expression::ImageQuery {
3139 image,
3140 query: ir::ImageQuery::Size { level },
3141 }
3142 }
3143 "textureNumLevels" => {
3144 let mut args = ctx.prepare_args(arguments, 1, span);
3145 let image = self.expression(args.next()?, ctx)?;
3146 args.finish()?;
3147
3148 ir::Expression::ImageQuery {
3149 image,
3150 query: ir::ImageQuery::NumLevels,
3151 }
3152 }
3153 "textureNumLayers" => {
3154 let mut args = ctx.prepare_args(arguments, 1, span);
3155 let image = self.expression(args.next()?, ctx)?;
3156 args.finish()?;
3157
3158 ir::Expression::ImageQuery {
3159 image,
3160 query: ir::ImageQuery::NumLayers,
3161 }
3162 }
3163 "textureNumSamples" => {
3164 let mut args = ctx.prepare_args(arguments, 1, span);
3165 let image = self.expression(args.next()?, ctx)?;
3166 args.finish()?;
3167
3168 ir::Expression::ImageQuery {
3169 image,
3170 query: ir::ImageQuery::NumSamples,
3171 }
3172 }
3173 "rayQueryInitialize" => {
3174 let mut args = ctx.prepare_args(arguments, 3, span);
3175 let query = self.ray_query_pointer(args.next()?, ctx)?;
3176 let acceleration_structure = self.expression(args.next()?, ctx)?;
3177 let descriptor = self.expression(args.next()?, ctx)?;
3178 args.finish()?;
3179
3180 let _ = ctx.module.generate_ray_desc_type();
3181 let fun = ir::RayQueryFunction::Initialize {
3182 acceleration_structure,
3183 descriptor,
3184 };
3185
3186 let rctx = ctx.runtime_expression_ctx(span)?;
3187 rctx.block
3188 .extend(rctx.emitter.finish(&rctx.function.expressions));
3189 rctx.emitter.start(&rctx.function.expressions);
3190 rctx.block
3191 .push(ir::Statement::RayQuery { query, fun }, span);
3192 return Ok(None);
3193 }
3194 "getCommittedHitVertexPositions" => {
3195 let mut args = ctx.prepare_args(arguments, 1, span);
3196 let query = self.ray_query_pointer(args.next()?, ctx)?;
3197 args.finish()?;
3198
3199 let _ = ctx.module.generate_vertex_return_type();
3200
3201 ir::Expression::RayQueryVertexPositions {
3202 query,
3203 committed: true,
3204 }
3205 }
3206 "getCandidateHitVertexPositions" => {
3207 let mut args = ctx.prepare_args(arguments, 1, span);
3208 let query = self.ray_query_pointer(args.next()?, ctx)?;
3209 args.finish()?;
3210
3211 let _ = ctx.module.generate_vertex_return_type();
3212
3213 ir::Expression::RayQueryVertexPositions {
3214 query,
3215 committed: false,
3216 }
3217 }
3218 "rayQueryProceed" => {
3219 let mut args = ctx.prepare_args(arguments, 1, span);
3220 let query = self.ray_query_pointer(args.next()?, ctx)?;
3221 args.finish()?;
3222
3223 let result =
3224 ctx.interrupt_emitter(ir::Expression::RayQueryProceedResult, span)?;
3225 let fun = ir::RayQueryFunction::Proceed { result };
3226 let rctx = ctx.runtime_expression_ctx(span)?;
3227 rctx.block
3228 .push(ir::Statement::RayQuery { query, fun }, span);
3229 return Ok(Some(result));
3230 }
3231 "rayQueryGenerateIntersection" => {
3232 let mut args = ctx.prepare_args(arguments, 2, span);
3233 let query = self.ray_query_pointer(args.next()?, ctx)?;
3234 let hit_t = self.expression(args.next()?, ctx)?;
3235 args.finish()?;
3236
3237 let fun = ir::RayQueryFunction::GenerateIntersection { hit_t };
3238 let rctx = ctx.runtime_expression_ctx(span)?;
3239 rctx.block
3240 .push(ir::Statement::RayQuery { query, fun }, span);
3241 return Ok(None);
3242 }
3243 "rayQueryConfirmIntersection" => {
3244 let mut args = ctx.prepare_args(arguments, 1, span);
3245 let query = self.ray_query_pointer(args.next()?, ctx)?;
3246 args.finish()?;
3247
3248 let fun = ir::RayQueryFunction::ConfirmIntersection;
3249 let rctx = ctx.runtime_expression_ctx(span)?;
3250 rctx.block
3251 .push(ir::Statement::RayQuery { query, fun }, span);
3252 return Ok(None);
3253 }
3254 "rayQueryTerminate" => {
3255 let mut args = ctx.prepare_args(arguments, 1, span);
3256 let query = self.ray_query_pointer(args.next()?, ctx)?;
3257 args.finish()?;
3258
3259 let fun = ir::RayQueryFunction::Terminate;
3260 let rctx = ctx.runtime_expression_ctx(span)?;
3261 rctx.block
3262 .push(ir::Statement::RayQuery { query, fun }, span);
3263 return Ok(None);
3264 }
3265 "rayQueryGetCommittedIntersection" => {
3266 let mut args = ctx.prepare_args(arguments, 1, span);
3267 let query = self.ray_query_pointer(args.next()?, ctx)?;
3268 args.finish()?;
3269
3270 let _ = ctx.module.generate_ray_intersection_type();
3271 ir::Expression::RayQueryGetIntersection {
3272 query,
3273 committed: true,
3274 }
3275 }
3276 "rayQueryGetCandidateIntersection" => {
3277 let mut args = ctx.prepare_args(arguments, 1, span);
3278 let query = self.ray_query_pointer(args.next()?, ctx)?;
3279 args.finish()?;
3280
3281 let _ = ctx.module.generate_ray_intersection_type();
3282 ir::Expression::RayQueryGetIntersection {
3283 query,
3284 committed: false,
3285 }
3286 }
3287 "RayDesc" => {
3288 let ty = ctx.module.generate_ray_desc_type();
3289 let handle = self.construct(
3290 span,
3291 &ast::ConstructorType::Type(ty),
3292 function.span,
3293 arguments,
3294 ctx,
3295 )?;
3296 return Ok(Some(handle));
3297 }
3298 "subgroupBallot" => {
3299 let mut args = ctx.prepare_args(arguments, 0, span);
3300 let predicate = if arguments.len() == 1 {
3301 Some(self.expression(args.next()?, ctx)?)
3302 } else {
3303 None
3304 };
3305 args.finish()?;
3306
3307 let result =
3308 ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, span)?;
3309 let rctx = ctx.runtime_expression_ctx(span)?;
3310 rctx.block
3311 .push(ir::Statement::SubgroupBallot { result, predicate }, span);
3312 return Ok(Some(result));
3313 }
3314 "quadSwapX" => {
3315 let mut args = ctx.prepare_args(arguments, 1, span);
3316
3317 let argument = self.expression(args.next()?, ctx)?;
3318 args.finish()?;
3319
3320 let ty = ctx.register_type(argument)?;
3321
3322 let result = ctx.interrupt_emitter(
3323 crate::Expression::SubgroupOperationResult { ty },
3324 span,
3325 )?;
3326 let rctx = ctx.runtime_expression_ctx(span)?;
3327 rctx.block.push(
3328 crate::Statement::SubgroupGather {
3329 mode: crate::GatherMode::QuadSwap(crate::Direction::X),
3330 argument,
3331 result,
3332 },
3333 span,
3334 );
3335 return Ok(Some(result));
3336 }
3337
3338 "quadSwapY" => {
3339 let mut args = ctx.prepare_args(arguments, 1, span);
3340
3341 let argument = self.expression(args.next()?, ctx)?;
3342 args.finish()?;
3343
3344 let ty = ctx.register_type(argument)?;
3345
3346 let result = ctx.interrupt_emitter(
3347 crate::Expression::SubgroupOperationResult { ty },
3348 span,
3349 )?;
3350 let rctx = ctx.runtime_expression_ctx(span)?;
3351 rctx.block.push(
3352 crate::Statement::SubgroupGather {
3353 mode: crate::GatherMode::QuadSwap(crate::Direction::Y),
3354 argument,
3355 result,
3356 },
3357 span,
3358 );
3359 return Ok(Some(result));
3360 }
3361
3362 "quadSwapDiagonal" => {
3363 let mut args = ctx.prepare_args(arguments, 1, span);
3364
3365 let argument = self.expression(args.next()?, ctx)?;
3366 args.finish()?;
3367
3368 let ty = ctx.register_type(argument)?;
3369
3370 let result = ctx.interrupt_emitter(
3371 crate::Expression::SubgroupOperationResult { ty },
3372 span,
3373 )?;
3374 let rctx = ctx.runtime_expression_ctx(span)?;
3375 rctx.block.push(
3376 crate::Statement::SubgroupGather {
3377 mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal),
3378 argument,
3379 result,
3380 },
3381 span,
3382 );
3383 return Ok(Some(result));
3384 }
3385 _ => {
3386 return Err(Box::new(Error::UnknownIdent(function.span, function.name)))
3387 }
3388 }
3389 };
3390
3391 let expr = ctx.append_expression(expr, span)?;
3392 Ok(Some(expr))
3393 }
3394 }
3395 }
3396
3397 fn math_function_helper(
3408 &mut self,
3409 span: Span,
3410 fun: ir::MathFunction,
3411 ast_arguments: &[Handle<ast::Expression<'source>>],
3412 ctx: &mut ExpressionContext<'source, '_, '_>,
3413 ) -> Result<'source, ir::Expression> {
3414 let mut lowered_arguments = Vec::with_capacity(ast_arguments.len());
3415 for &arg in ast_arguments {
3416 let lowered = self.expression_for_abstract(arg, ctx)?;
3417 ctx.grow_types(lowered)?;
3418 lowered_arguments.push(lowered);
3419 }
3420
3421 let fun_overloads = fun.overloads();
3422 let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?;
3423 self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?;
3424
3425 if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion {
3429 ctx.module.generate_predeclared_type(predeclared);
3430 }
3431
3432 Ok(ir::Expression::Math {
3433 fun,
3434 arg: lowered_arguments[0],
3435 arg1: lowered_arguments.get(1).cloned(),
3436 arg2: lowered_arguments.get(2).cloned(),
3437 arg3: lowered_arguments.get(3).cloned(),
3438 })
3439 }
3440
3441 fn resolve_overloads<O, F>(
3452 &self,
3453 span: Span,
3454 fun: F,
3455 overloads: O,
3456 arguments: &[Handle<ir::Expression>],
3457 ctx: &ExpressionContext<'source, '_, '_>,
3458 ) -> Result<'source, proc::Rule>
3459 where
3460 O: proc::OverloadSet,
3461 F: TryToWgsl + core::fmt::Debug + Copy,
3462 {
3463 let mut remaining_overloads = overloads.clone();
3464 let min_arguments = remaining_overloads.min_arguments();
3465 let max_arguments = remaining_overloads.max_arguments();
3466 if arguments.len() < min_arguments {
3467 return Err(Box::new(Error::WrongArgumentCount {
3468 span,
3469 expected: min_arguments as u32..max_arguments as u32,
3470 found: arguments.len() as u32,
3471 }));
3472 }
3473 if arguments.len() > max_arguments {
3474 return Err(Box::new(Error::TooManyArguments {
3475 function: fun.to_wgsl_for_diagnostics(),
3476 call_span: span,
3477 arg_span: ctx.get_expression_span(arguments[max_arguments]),
3478 max_arguments: max_arguments as _,
3479 }));
3480 }
3481
3482 log::debug!(
3483 "Initial overloads: {:#?}",
3484 remaining_overloads.for_debug(&ctx.module.types)
3485 );
3486
3487 for (arg_index, &arg) in arguments.iter().enumerate() {
3488 let arg_type_resolution = &ctx.typifier()[arg];
3489 let arg_inner = arg_type_resolution.inner_with(&ctx.module.types);
3490 log::debug!(
3491 "Supplying argument {arg_index} of type {:?}",
3492 arg_type_resolution.for_debug(&ctx.module.types)
3493 );
3494 let next_remaining_overloads =
3495 remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types);
3496
3497 log::debug!(
3505 "Remaining overloads: {:#?}",
3506 next_remaining_overloads.for_debug(&ctx.module.types)
3507 );
3508
3509 if next_remaining_overloads.is_empty() {
3512 let function = fun.to_wgsl_for_diagnostics();
3513 let call_span = span;
3514 let arg_span = ctx.get_expression_span(arg);
3515 let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string();
3516
3517 let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types);
3520 if only_this_argument.is_empty() {
3521 let allowed: Vec<String> = overloads
3525 .allowed_args(arg_index, &ctx.module.to_ctx())
3526 .iter()
3527 .map(|ty| ctx.type_resolution_to_string(ty))
3528 .collect();
3529
3530 if allowed.is_empty() {
3531 unreachable!("expected all overloads to have the same arity");
3537 }
3538
3539 return Err(Box::new(Error::WrongArgumentType {
3542 function,
3543 call_span,
3544 arg_span,
3545 arg_index: arg_index as u32,
3546 arg_ty,
3547 allowed,
3548 }));
3549 }
3550
3551 let allowed: Vec<String> = remaining_overloads
3559 .allowed_args(arg_index, &ctx.module.to_ctx())
3560 .iter()
3561 .map(|ty| ctx.type_resolution_to_string(ty))
3562 .collect();
3563
3564 let mut remaining_overloads = overloads;
3567 for (prior_index, &prior_expr) in arguments.iter().enumerate() {
3568 let prior_type_resolution = &ctx.typifier()[prior_expr];
3569 let prior_ty = prior_type_resolution.inner_with(&ctx.module.types);
3570 remaining_overloads =
3571 remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types);
3572 if remaining_overloads
3573 .arg(arg_index, arg_inner, &ctx.module.types)
3574 .is_empty()
3575 {
3576 let inconsistent_span = ctx.get_expression_span(arguments[prior_index]);
3578 let inconsistent_ty =
3579 ctx.as_diagnostic_display(prior_type_resolution).to_string();
3580
3581 if allowed.is_empty() {
3582 unreachable!("expected all overloads to have the same arity");
3589 }
3590
3591 return Err(Box::new(Error::InconsistentArgumentType {
3593 function,
3594 call_span,
3595 arg_span,
3596 arg_index: arg_index as u32,
3597 arg_ty,
3598 inconsistent_span,
3599 inconsistent_index: prior_index as u32,
3600 inconsistent_ty,
3601 allowed,
3602 }));
3603 }
3604 }
3605 unreachable!("Failed to eliminate argument type when re-tried");
3606 }
3607 remaining_overloads = next_remaining_overloads;
3608 }
3609
3610 Ok(remaining_overloads.most_preferred())
3613 }
3614
3615 fn apply_automatic_conversions_for_call(
3621 &self,
3622 rule: &proc::Rule,
3623 arguments: &mut [Handle<ir::Expression>],
3624 ctx: &mut ExpressionContext<'source, '_, '_>,
3625 ) -> Result<'source, ()> {
3626 for (i, argument) in arguments.iter_mut().enumerate() {
3627 let goal_inner = rule.arguments[i].inner_with(&ctx.module.types);
3628 let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) {
3629 Some(goal_scalar) => {
3630 let arg_span = ctx.get_expression_span(*argument);
3631 ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)?
3632 }
3633 None => *argument,
3635 };
3636
3637 *argument = converted;
3638 }
3639
3640 Ok(())
3641 }
3642
3643 fn atomic_pointer(
3644 &mut self,
3645 expr: Handle<ast::Expression<'source>>,
3646 ctx: &mut ExpressionContext<'source, '_, '_>,
3647 ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> {
3648 let span = ctx.ast_expressions.get_span(expr);
3649 let pointer = self.expression(expr, ctx)?;
3650
3651 match *resolve_inner!(ctx, pointer) {
3652 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
3653 ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)),
3654 ref other => {
3655 log::error!("Pointer type to {other:?} passed to atomic op");
3656 Err(Box::new(Error::InvalidAtomicPointer(span)))
3657 }
3658 },
3659 ref other => {
3660 log::error!("Type {other:?} passed to atomic op");
3661 Err(Box::new(Error::InvalidAtomicPointer(span)))
3662 }
3663 }
3664 }
3665
3666 fn atomic_helper(
3667 &mut self,
3668 span: Span,
3669 fun: ir::AtomicFunction,
3670 args: &[Handle<ast::Expression<'source>>],
3671 is_statement: bool,
3672 ctx: &mut ExpressionContext<'source, '_, '_>,
3673 ) -> Result<'source, Option<Handle<ir::Expression>>> {
3674 let mut args = ctx.prepare_args(args, 2, span);
3675
3676 let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3677 let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3678 let value_inner = resolve_inner!(ctx, value);
3679 args.finish()?;
3680
3681 let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max)
3686 && matches!(
3687 *value_inner,
3688 ir::TypeInner::Scalar(ir::Scalar { width: 8, .. })
3689 );
3690 let result = if is_64_bit_min_max && is_statement {
3691 let rctx = ctx.runtime_expression_ctx(span)?;
3692 rctx.block
3693 .extend(rctx.emitter.finish(&rctx.function.expressions));
3694 rctx.emitter.start(&rctx.function.expressions);
3695 None
3696 } else {
3697 let ty = ctx.register_type(value)?;
3698 Some(ctx.interrupt_emitter(
3699 ir::Expression::AtomicResult {
3700 ty,
3701 comparison: false,
3702 },
3703 span,
3704 )?)
3705 };
3706 let rctx = ctx.runtime_expression_ctx(span)?;
3707 rctx.block.push(
3708 ir::Statement::Atomic {
3709 pointer,
3710 fun,
3711 value,
3712 result,
3713 },
3714 span,
3715 );
3716 Ok(result)
3717 }
3718
3719 fn texture_sample_helper(
3720 &mut self,
3721 fun: Texture,
3722 args: &[Handle<ast::Expression<'source>>],
3723 span: Span,
3724 ctx: &mut ExpressionContext<'source, '_, '_>,
3725 ) -> Result<'source, ir::Expression> {
3726 let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);
3727
3728 fn get_image_and_span<'source>(
3729 lowerer: &mut Lowerer<'source, '_>,
3730 args: &mut ArgumentContext<'_, 'source>,
3731 ctx: &mut ExpressionContext<'source, '_, '_>,
3732 ) -> Result<'source, (Handle<ir::Expression>, Span)> {
3733 let image = args.next()?;
3734 let image_span = ctx.ast_expressions.get_span(image);
3735 let image = lowerer.expression_for_abstract(image, ctx)?;
3736 Ok((image, image_span))
3737 }
3738
3739 let image;
3740 let image_span;
3741 let gather;
3742 match fun {
3743 Texture::Gather => {
3744 let image_or_component = args.next()?;
3745 let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);
3746 let lowered_image_or_component = self.expression(image_or_component, ctx)?;
3748
3749 match *resolve_inner!(ctx, lowered_image_or_component) {
3750 ir::TypeInner::Image {
3751 class: ir::ImageClass::Depth { .. },
3752 ..
3753 } => {
3754 image = lowered_image_or_component;
3755 image_span = image_or_component_span;
3756 gather = Some(ir::SwizzleComponent::X);
3757 }
3758 _ => {
3759 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
3760 gather = Some(ctx.gather_component(
3761 lowered_image_or_component,
3762 image_or_component_span,
3763 span,
3764 )?);
3765 }
3766 }
3767 }
3768 Texture::GatherCompare => {
3769 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
3770 gather = Some(ir::SwizzleComponent::X);
3771 }
3772
3773 _ => {
3774 (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
3775 gather = None;
3776 }
3777 };
3778
3779 let sampler = self.expression_for_abstract(args.next()?, ctx)?;
3780
3781 let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3782 let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge);
3783
3784 let (class, arrayed) = ctx.image_data(image, image_span)?;
3785 let array_index = arrayed
3786 .then(|| self.expression(args.next()?, ctx))
3787 .transpose()?;
3788
3789 let level;
3790 let depth_ref;
3791 match fun {
3792 Texture::Gather => {
3793 level = ir::SampleLevel::Zero;
3794 depth_ref = None;
3795 }
3796 Texture::GatherCompare => {
3797 let reference =
3798 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3799 level = ir::SampleLevel::Zero;
3800 depth_ref = Some(reference);
3801 }
3802
3803 Texture::Sample => {
3804 level = ir::SampleLevel::Auto;
3805 depth_ref = None;
3806 }
3807 Texture::SampleBias => {
3808 let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3809 level = ir::SampleLevel::Bias(bias);
3810 depth_ref = None;
3811 }
3812 Texture::SampleCompare => {
3813 let reference =
3814 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3815 level = ir::SampleLevel::Auto;
3816 depth_ref = Some(reference);
3817 }
3818 Texture::SampleCompareLevel => {
3819 let reference =
3820 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3821 level = ir::SampleLevel::Zero;
3822 depth_ref = Some(reference);
3823 }
3824 Texture::SampleGrad => {
3825 let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3826 let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
3827 level = ir::SampleLevel::Gradient { x, y };
3828 depth_ref = None;
3829 }
3830 Texture::SampleLevel => {
3831 let exact = match class {
3832 ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?,
3835
3836 ir::ImageClass::Sampled { .. } => {
3839 self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?
3840 }
3841
3842 ir::ImageClass::Storage { .. } | ir::ImageClass::External => {
3846 self.expression(args.next()?, ctx)?
3847 }
3848 };
3849 level = ir::SampleLevel::Exact(exact);
3850 depth_ref = None;
3851 }
3852 Texture::SampleBaseClampToEdge => {
3853 level = crate::SampleLevel::Zero;
3854 depth_ref = None;
3855 }
3856 };
3857
3858 let offset = args
3859 .next()
3860 .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const()))
3861 .ok()
3862 .transpose()?;
3863
3864 args.finish()?;
3865
3866 Ok(ir::Expression::ImageSample {
3867 image,
3868 sampler,
3869 gather,
3870 coordinate,
3871 array_index,
3872 offset,
3873 level,
3874 depth_ref,
3875 clamp_to_edge,
3876 })
3877 }
3878
3879 fn subgroup_operation_helper(
3880 &mut self,
3881 span: Span,
3882 op: ir::SubgroupOperation,
3883 collective_op: ir::CollectiveOperation,
3884 arguments: &[Handle<ast::Expression<'source>>],
3885 ctx: &mut ExpressionContext<'source, '_, '_>,
3886 ) -> Result<'source, Handle<ir::Expression>> {
3887 let mut args = ctx.prepare_args(arguments, 1, span);
3888
3889 let argument = self.expression(args.next()?, ctx)?;
3890 args.finish()?;
3891
3892 let ty = ctx.register_type(argument)?;
3893
3894 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
3895 let rctx = ctx.runtime_expression_ctx(span)?;
3896 rctx.block.push(
3897 ir::Statement::SubgroupCollectiveOperation {
3898 op,
3899 collective_op,
3900 argument,
3901 result,
3902 },
3903 span,
3904 );
3905 Ok(result)
3906 }
3907
3908 fn subgroup_gather_helper(
3909 &mut self,
3910 span: Span,
3911 mode: SubgroupGather,
3912 arguments: &[Handle<ast::Expression<'source>>],
3913 ctx: &mut ExpressionContext<'source, '_, '_>,
3914 ) -> Result<'source, Handle<ir::Expression>> {
3915 let mut args = ctx.prepare_args(arguments, 2, span);
3916
3917 let argument = self.expression(args.next()?, ctx)?;
3918
3919 use SubgroupGather as Sg;
3920 let mode = if let Sg::BroadcastFirst = mode {
3921 ir::GatherMode::BroadcastFirst
3922 } else {
3923 let index = self.expression(args.next()?, ctx)?;
3924 match mode {
3925 Sg::BroadcastFirst => unreachable!(),
3926 Sg::Broadcast => ir::GatherMode::Broadcast(index),
3927 Sg::Shuffle => ir::GatherMode::Shuffle(index),
3928 Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index),
3929 Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index),
3930 Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index),
3931 Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index),
3932 }
3933 };
3934
3935 args.finish()?;
3936
3937 let ty = ctx.register_type(argument)?;
3938
3939 let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
3940 let rctx = ctx.runtime_expression_ctx(span)?;
3941 rctx.block.push(
3942 ir::Statement::SubgroupGather {
3943 mode,
3944 argument,
3945 result,
3946 },
3947 span,
3948 );
3949 Ok(result)
3950 }
3951
3952 fn r#struct(
3953 &mut self,
3954 s: &ast::Struct<'source>,
3955 span: Span,
3956 ctx: &mut GlobalContext<'source, '_, '_>,
3957 ) -> Result<'source, Handle<ir::Type>> {
3958 let mut offset = 0;
3959 let mut struct_alignment = proc::Alignment::ONE;
3960 let mut members = Vec::with_capacity(s.members.len());
3961
3962 let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new();
3963
3964 for member in s.members.iter() {
3965 let ty = self.resolve_ast_type(member.ty, &mut ctx.as_const())?;
3966
3967 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
3968 let LayoutErrorInner::TooLarge = err.inner else {
3969 unreachable!("unexpected layout error: {err:?}");
3970 };
3971 if ty == err.ty {
3977 Box::new(Error::StructMemberTooLarge {
3978 member_name_span: member.name.span,
3979 })
3980 } else {
3981 Box::new(Error::TypeTooLarge {
3984 span: ctx.module.types.get_span(err.ty),
3985 })
3986 }
3987 })?;
3988
3989 let member_min_size = ctx.layouter[ty].size;
3990 let member_min_alignment = ctx.layouter[ty].alignment;
3991
3992 let member_size = if let Some(size_expr) = member.size {
3993 let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;
3994 if size < member_min_size {
3995 return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size)));
3996 } else {
3997 size
3998 }
3999 } else {
4000 member_min_size
4001 };
4002
4003 let member_alignment = if let Some(align_expr) = member.align {
4004 let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;
4005 if let Some(alignment) = proc::Alignment::new(align) {
4006 if alignment < member_min_alignment {
4007 return Err(Box::new(Error::AlignAttributeTooLow(
4008 span,
4009 member_min_alignment,
4010 )));
4011 } else {
4012 alignment
4013 }
4014 } else {
4015 return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span)));
4016 }
4017 } else {
4018 member_min_alignment
4019 };
4020
4021 let binding = self.binding(&member.binding, ty, ctx)?;
4022
4023 offset = member_alignment.round_up(offset);
4024 struct_alignment = struct_alignment.max(member_alignment);
4025
4026 if !member.doc_comments.is_empty() {
4027 doc_comments.push(Some(
4028 member.doc_comments.iter().map(|s| s.to_string()).collect(),
4029 ));
4030 }
4031 members.push(ir::StructMember {
4032 name: Some(member.name.name.to_owned()),
4033 ty,
4034 binding,
4035 offset,
4036 });
4037
4038 offset += member_size;
4039 if offset > crate::valid::MAX_TYPE_SIZE {
4040 return Err(Box::new(Error::TypeTooLarge { span }));
4041 }
4042 }
4043
4044 let size = struct_alignment.round_up(offset);
4045 let inner = ir::TypeInner::Struct {
4046 members,
4047 span: size,
4048 };
4049
4050 let handle = ctx.module.types.insert(
4051 ir::Type {
4052 name: Some(s.name.name.to_string()),
4053 inner,
4054 },
4055 span,
4056 );
4057 for (i, c) in doc_comments.drain(..).enumerate() {
4058 if let Some(comment) = c {
4059 ctx.module
4060 .get_or_insert_default_doc_comments()
4061 .struct_members
4062 .insert((handle, i), comment);
4063 }
4064 }
4065 Ok(handle)
4066 }
4067
4068 fn const_u32(
4069 &mut self,
4070 expr: Handle<ast::Expression<'source>>,
4071 ctx: &mut ExpressionContext<'source, '_, '_>,
4072 ) -> Result<'source, (u32, Span)> {
4073 let span = ctx.ast_expressions.get_span(expr);
4074 let expr = self.expression(expr, ctx)?;
4075 let value = ctx
4076 .module
4077 .to_ctx()
4078 .eval_expr_to_u32(expr)
4079 .map_err(|err| match err {
4080 proc::U32EvalError::NonConst => Error::ExpectedConstExprConcreteIntegerScalar(span),
4081 proc::U32EvalError::Negative => Error::ExpectedNonNegative(span),
4082 })?;
4083 Ok((value, span))
4084 }
4085
4086 fn array_size(
4087 &mut self,
4088 size: ast::ArraySize<'source>,
4089 ctx: &mut ExpressionContext<'source, '_, '_>,
4090 ) -> Result<'source, ir::ArraySize> {
4091 Ok(match size {
4092 ast::ArraySize::Constant(expr) => {
4093 let span = ctx.ast_expressions.get_span(expr);
4094 let const_expr = self.expression(expr, &mut ctx.as_const());
4095 match const_expr {
4096 Ok(value) => {
4097 let len = ctx.const_eval_expr_to_u32(value).map_err(|err| {
4098 Box::new(match err {
4099 proc::U32EvalError::NonConst => {
4100 Error::ExpectedConstExprConcreteIntegerScalar(span)
4101 }
4102 proc::U32EvalError::Negative => {
4103 Error::ExpectedPositiveArrayLength(span)
4104 }
4105 })
4106 })?;
4107 let size =
4108 NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;
4109 ir::ArraySize::Constant(size)
4110 }
4111 Err(err) => {
4112 if let Error::ConstantEvaluatorError(ref ty, _) = *err {
4113 match **ty {
4114 proc::ConstantEvaluatorError::OverrideExpr => {
4115 ir::ArraySize::Pending(self.array_size_override(
4116 expr,
4117 &mut ctx.as_global().as_override(),
4118 span,
4119 )?)
4120 }
4121 _ => {
4122 return Err(err);
4123 }
4124 }
4125 } else {
4126 return Err(err);
4127 }
4128 }
4129 }
4130 }
4131 ast::ArraySize::Dynamic => ir::ArraySize::Dynamic,
4132 })
4133 }
4134
4135 fn array_size_override(
4136 &mut self,
4137 size_expr: Handle<ast::Expression<'source>>,
4138 ctx: &mut ExpressionContext<'source, '_, '_>,
4139 span: Span,
4140 ) -> Result<'source, Handle<ir::Override>> {
4141 let expr = self.expression(size_expr, ctx)?;
4142 match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
4143 Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({
4144 if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] {
4145 handle
4146 } else {
4147 let ty = ctx.register_type(expr)?;
4148 ctx.module.overrides.append(
4149 ir::Override {
4150 name: None,
4151 id: None,
4152 ty,
4153 init: Some(expr),
4154 },
4155 span,
4156 )
4157 }
4158 }),
4159 _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
4160 span,
4161 ))),
4162 }
4163 }
4164
4165 fn resolve_named_ast_type(
4175 &mut self,
4176 handle: Handle<ast::Type<'source>>,
4177 name: Option<String>,
4178 ctx: &mut ExpressionContext<'source, '_, '_>,
4179 ) -> Result<'source, Handle<ir::Type>> {
4180 let inner = match ctx.types[handle] {
4181 ast::Type::Scalar(scalar) => scalar.to_inner_scalar(),
4182 ast::Type::Vector { size, ty, ty_span } => {
4183 let ty = self.resolve_ast_type(ty, ctx)?;
4184 let scalar = match ctx.module.types[ty].inner {
4185 ir::TypeInner::Scalar(sc) => sc,
4186 _ => return Err(Box::new(Error::UnknownScalarType(ty_span))),
4187 };
4188 ir::TypeInner::Vector { size, scalar }
4189 }
4190 ast::Type::Matrix {
4191 rows,
4192 columns,
4193 ty,
4194 ty_span,
4195 } => {
4196 let ty = self.resolve_ast_type(ty, ctx)?;
4197 let scalar = match ctx.module.types[ty].inner {
4198 ir::TypeInner::Scalar(sc) => sc,
4199 _ => return Err(Box::new(Error::UnknownScalarType(ty_span))),
4200 };
4201 match scalar.kind {
4202 ir::ScalarKind::Float => ir::TypeInner::Matrix {
4203 columns,
4204 rows,
4205 scalar,
4206 },
4207 _ => return Err(Box::new(Error::BadMatrixScalarKind(ty_span, scalar))),
4208 }
4209 }
4210 ast::Type::Atomic(scalar) => scalar.to_inner_atomic(),
4211 ast::Type::Pointer { base, space } => {
4212 let base = self.resolve_ast_type(base, ctx)?;
4213 ir::TypeInner::Pointer { base, space }
4214 }
4215 ast::Type::Array { base, size } => {
4216 let base = self.resolve_ast_type(base, &mut ctx.as_const())?;
4217 let size = self.array_size(size, ctx)?;
4218
4219 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
4221 let LayoutErrorInner::TooLarge = err.inner else {
4222 unreachable!("unexpected layout error: {err:?}");
4223 };
4224 Box::new(Error::TypeTooLarge {
4227 span: ctx.module.types.get_span(err.ty),
4228 })
4229 })?;
4230 let stride = ctx.layouter[base].to_stride();
4231
4232 ir::TypeInner::Array { base, size, stride }
4233 }
4234 ast::Type::Image {
4235 dim,
4236 arrayed,
4237 class,
4238 } => {
4239 if class == crate::ImageClass::External {
4240 ctx.module.generate_external_texture_types();
4255 }
4256 ir::TypeInner::Image {
4257 dim,
4258 arrayed,
4259 class,
4260 }
4261 }
4262 ast::Type::Sampler { comparison } => ir::TypeInner::Sampler { comparison },
4263 ast::Type::AccelerationStructure { vertex_return } => {
4264 ir::TypeInner::AccelerationStructure { vertex_return }
4265 }
4266 ast::Type::RayQuery { vertex_return } => ir::TypeInner::RayQuery { vertex_return },
4267 ast::Type::BindingArray { base, size } => {
4268 let base = self.resolve_ast_type(base, ctx)?;
4269 let size = self.array_size(size, ctx)?;
4270 ir::TypeInner::BindingArray { base, size }
4271 }
4272 ast::Type::RayDesc => {
4273 return Ok(ctx.module.generate_ray_desc_type());
4274 }
4275 ast::Type::RayIntersection => {
4276 return Ok(ctx.module.generate_ray_intersection_type());
4277 }
4278 ast::Type::User(ref ident) => {
4279 return match ctx.globals.get(ident.name) {
4280 Some(&LoweredGlobalDecl::Type(handle)) => Ok(handle),
4281 Some(_) => Err(Box::new(Error::Unexpected(ident.span, ExpectedToken::Type))),
4282 None => Err(Box::new(Error::UnknownType(ident.span))),
4283 }
4284 }
4285 };
4286
4287 Ok(ctx.as_global().ensure_type_exists(name, inner))
4288 }
4289
4290 fn resolve_ast_type(
4292 &mut self,
4293 handle: Handle<ast::Type<'source>>,
4294 ctx: &mut ExpressionContext<'source, '_, '_>,
4295 ) -> Result<'source, Handle<ir::Type>> {
4296 self.resolve_named_ast_type(handle, None, ctx)
4297 }
4298
4299 fn binding(
4300 &mut self,
4301 binding: &Option<ast::Binding<'source>>,
4302 ty: Handle<ir::Type>,
4303 ctx: &mut GlobalContext<'source, '_, '_>,
4304 ) -> Result<'source, Option<ir::Binding>> {
4305 Ok(match *binding {
4306 Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)),
4307 Some(ast::Binding::Location {
4308 location,
4309 interpolation,
4310 sampling,
4311 blend_src,
4312 per_primitive,
4313 }) => {
4314 let blend_src = if let Some(blend_src) = blend_src {
4315 Some(self.const_u32(blend_src, &mut ctx.as_const())?.0)
4316 } else {
4317 None
4318 };
4319
4320 let mut binding = ir::Binding::Location {
4321 location: self.const_u32(location, &mut ctx.as_const())?.0,
4322 interpolation,
4323 sampling,
4324 blend_src,
4325 per_primitive,
4326 };
4327 binding.apply_default_interpolation(&ctx.module.types[ty].inner);
4328 Some(binding)
4329 }
4330 None => None,
4331 })
4332 }
4333
4334 fn ray_query_pointer(
4335 &mut self,
4336 expr: Handle<ast::Expression<'source>>,
4337 ctx: &mut ExpressionContext<'source, '_, '_>,
4338 ) -> Result<'source, Handle<ir::Expression>> {
4339 let span = ctx.ast_expressions.get_span(expr);
4340 let pointer = self.expression(expr, ctx)?;
4341
4342 match *resolve_inner!(ctx, pointer) {
4343 ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4344 ir::TypeInner::RayQuery { .. } => Ok(pointer),
4345 ref other => {
4346 log::error!("Pointer type to {other:?} passed to ray query op");
4347 Err(Box::new(Error::InvalidRayQueryPointer(span)))
4348 }
4349 },
4350 ref other => {
4351 log::error!("Type {other:?} passed to ray query op");
4352 Err(Box::new(Error::InvalidRayQueryPointer(span)))
4353 }
4354 }
4355 }
4356}
4357
4358impl ir::AtomicFunction {
4359 pub fn map(word: &str) -> Option<Self> {
4360 Some(match word {
4361 "atomicAdd" => ir::AtomicFunction::Add,
4362 "atomicSub" => ir::AtomicFunction::Subtract,
4363 "atomicAnd" => ir::AtomicFunction::And,
4364 "atomicOr" => ir::AtomicFunction::InclusiveOr,
4365 "atomicXor" => ir::AtomicFunction::ExclusiveOr,
4366 "atomicMin" => ir::AtomicFunction::Min,
4367 "atomicMax" => ir::AtomicFunction::Max,
4368 "atomicExchange" => ir::AtomicFunction::Exchange { compare: None },
4369 _ => return None,
4370 })
4371 }
4372}