1use std::{collections::HashMap, fmt::Display, iter::zip};
2use wgsl_types::{
3 ShaderStage,
4 builtin::{call_builtin_fn, is_ctor, struct_ctor},
5 conv::Convert,
6 inst::{Instance, LiteralInstance, RefInstance, VecInstance},
7 syntax::{AccessMode, AddressSpace},
8 tplt::TpltParam,
9 ty::{Ty, Type},
10};
11
12use crate::eval::PRELUDE;
13
14use super::{
15 ATTR_INTRINSIC, Context, Eval, EvalError, EvalTy, ScopeKind, SyntaxUtil, attrs::EvalAttrs,
16 eval_tplt_arg, ty_eval_ty,
17};
18
19use wgsl_parse::{SyntaxNode, span::Spanned, syntax::*};
20
21type E = EvalError;
22
23#[derive(Clone, Debug, PartialEq)]
25pub enum Flow {
26 Next,
27 Break,
28 Continue,
29 Return(Option<Instance>),
30}
31
32impl From<Instance> for Flow {
33 fn from(inst: Instance) -> Self {
34 Self::Return(Some(inst))
35 }
36}
37
38macro_rules! with_stage {
39 ($ctx:expr, $stage:expr, $body:tt) => {{
40 let stage = $ctx.stage;
41 $ctx.stage = $stage;
42 #[allow(clippy::redundant_closure_call)]
43 let body = (|| $body)();
44 $ctx.stage = stage;
45 body
46 }};
47}
48
49#[derive(Clone, Copy)]
56pub(crate) enum CompoundScope {
57 Regular,
58 Transparent,
59 Leaking,
60}
61
62impl CompoundScope {
63 pub(crate) fn push(&self, ctx: &mut Context) {
64 match self {
65 CompoundScope::Regular => {
66 ctx.scope.push();
67 }
68 CompoundScope::Transparent => {
69 ctx.scope.push_transparent();
70 }
71 CompoundScope::Leaking => {}
72 }
73 }
74
75 pub(crate) fn pop(&self, ctx: &mut Context) {
76 match self {
77 CompoundScope::Regular | CompoundScope::Transparent => {
78 ctx.scope.pop();
79 }
80 CompoundScope::Leaking => {}
81 }
82 }
83}
84
85macro_rules! with_scope {
86 ($ctx:expr, $body:tt) => {{ with_scope!($ctx, CompoundScope::Regular, $body) }};
87 ($ctx:expr, $scoping:expr, $body:tt) => {{
88 $scoping.push($ctx);
89 #[allow(clippy::redundant_closure_call)]
90 let body = (|| $body)();
91 $scoping.pop($ctx);
92 body
93 }};
94}
95
96macro_rules! module_scope {
97 ($ctx:expr, $body:tt) => {{
98 assert!($ctx.scope.is_root());
99 let kind = $ctx.kind;
100 $ctx.kind = ScopeKind::Module;
101 #[allow(clippy::redundant_closure_call)]
102 let body = (|| $body)();
103 $ctx.kind = kind;
104 body
105 }};
106}
107
108macro_rules! function_scope {
109 ($ctx:expr, $body:tt) => {{
110 assert!(!$ctx.scope.is_root());
111 let kind = $ctx.kind;
112 $ctx.kind = ScopeKind::Function;
113 #[allow(clippy::redundant_closure_call)]
114 let body = (|| $body)();
115 $ctx.kind = kind;
116 body
117 }};
118}
119
120pub(super) use with_scope;
121pub(super) use with_stage;
122
123impl Display for Flow {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 match self {
126 Flow::Next => write!(f, "void"),
127 Flow::Break => write!(f, "break"),
128 Flow::Continue => write!(f, "continue"),
129 Flow::Return(_) => write!(f, "return"),
130 }
131 }
132}
133
134pub trait Exec {
135 fn exec(&self, ctx: &mut Context) -> Result<Flow, E>;
136}
137
138impl<T: Exec> Exec for Spanned<T> {
139 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
140 self.node().exec(ctx).inspect_err(|_| {
141 ctx.set_err_span_ctx(self.span());
142 })
143 }
144}
145
146impl Exec for TranslationUnit {
147 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
148 module_scope!(ctx, {
149 for decl in PRELUDE
150 .global_declarations
151 .iter()
152 .chain(&ctx.source.global_declarations)
153 {
154 let flow = decl.exec(ctx)?;
155 match flow {
156 Flow::Next => (),
157 Flow::Break | Flow::Continue | Flow::Return(_) => {
158 if let Some(ident) = decl.ident() {
159 ctx.set_err_decl_ctx(ident.to_string());
160 }
161 return Err(E::FlowInModule(flow));
162 }
163 }
164 }
165
166 Ok(Flow::Next)
167 })
168 }
169}
170
171impl Exec for GlobalDeclaration {
172 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
173 match self {
174 GlobalDeclaration::Declaration(decl) => {
175 if ctx.scope.contains(&decl.ident.name()) {
176 Ok(Flow::Next)
179 } else {
180 decl.exec(ctx)
181 }
182 }
183 GlobalDeclaration::ConstAssert(decl) => decl.exec(ctx),
184 _ => Ok(Flow::Next),
185 }
186 .inspect_err(|_| {
187 if let Some(ident) = self.ident() {
188 ctx.set_err_decl_ctx(ident.to_string());
189 }
190 })
191 }
192}
193
194impl Exec for Statement {
195 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
196 match self {
197 Statement::Void => Ok(Flow::Next),
198 Statement::Compound(s) => s.exec(ctx),
199 Statement::Assignment(s) => s.exec(ctx),
200 Statement::Increment(s) => s.exec(ctx),
201 Statement::Decrement(s) => s.exec(ctx),
202 Statement::If(s) => s.exec(ctx),
203 Statement::Switch(s) => s.exec(ctx),
204 Statement::Loop(s) => s.exec(ctx),
205 Statement::For(s) => s.exec(ctx),
206 Statement::While(s) => s.exec(ctx),
207 Statement::Break(s) => s.exec(ctx),
208 Statement::Continue(s) => s.exec(ctx),
209 Statement::Return(s) => s.exec(ctx),
210 Statement::Discard(s) => s.exec(ctx),
211 Statement::FunctionCall(s) => s.exec(ctx),
212 Statement::ConstAssert(s) => s.exec(ctx),
213 Statement::Declaration(s) => s.exec(ctx),
214 }
215 }
216}
217
218impl Exec for CompoundStatement {
219 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
220 compound_exec(self, ctx, CompoundScope::Regular)
221 }
222}
223
224pub(crate) fn compound_exec(
225 stmt: &CompoundStatement,
226 ctx: &mut Context,
227 scoping: CompoundScope,
228) -> Result<Flow, E> {
229 with_scope!(ctx, scoping, {
230 for stmt in &stmt.statements {
231 let flow = stmt.exec(ctx)?;
232 match flow {
233 Flow::Next => (),
234 Flow::Break | Flow::Continue | Flow::Return(_) => {
235 return Ok(flow);
236 }
237 }
238 }
239 Ok(Flow::Next)
240 })
241}
242
243impl Exec for AssignmentStatement {
244 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
245 let is_phony = matches!(self.lhs.node(), Expression::TypeOrIdentifier(TypeExpression { path: None, ident, template_args: None }) if *ident.name() == "_");
246 if self.operator == AssignmentOperator::Equal && is_phony {
247 let _ = self.rhs.eval(ctx)?;
248 return Ok(Flow::Next);
249 }
250
251 let lhs = self.lhs.eval(ctx)?;
252
253 if let Instance::Ref(r) = lhs {
254 let rhs = self.rhs.eval_value(ctx)?;
255 match self.operator {
256 AssignmentOperator::Equal => {
257 let rhs = rhs
258 .convert_to(&r.ty)
259 .ok_or_else(|| E::AssignType(rhs.ty(), r.ty.clone()))?;
260 r.write(rhs)?;
261 }
262 AssignmentOperator::PlusEqual => {
263 let val = r.read()?.op_add(&rhs, ctx.stage)?;
264 r.write(val)?;
265 }
266 AssignmentOperator::MinusEqual => {
267 let val = r.read()?.op_sub(&rhs, ctx.stage)?;
268 r.write(val)?;
269 }
270 AssignmentOperator::TimesEqual => {
271 let val = r.read()?.op_mul(&rhs, ctx.stage)?;
272 r.write(val)?;
273 }
274 AssignmentOperator::DivisionEqual => {
275 let val = r.read()?.op_div(&rhs, ctx.stage)?;
276 r.write(val)?;
277 }
278 AssignmentOperator::ModuloEqual => {
279 let val = r.read()?.op_rem(&rhs, ctx.stage)?;
280 r.write(val)?;
281 }
282 AssignmentOperator::AndEqual => {
283 let val = r.read()?.op_bitand(&rhs)?;
284 r.write(val)?;
285 }
286 AssignmentOperator::OrEqual => {
287 let val = r.read()?.op_bitor(&rhs)?;
288 r.write(val)?;
289 }
290 AssignmentOperator::XorEqual => {
291 let val = r.read()?.op_bitxor(&rhs)?;
292 r.write(val)?;
293 }
294 AssignmentOperator::ShiftRightAssign => {
295 let val = r.read()?.op_shr(&rhs, ctx.stage)?;
296 r.write(val)?;
297 }
298 AssignmentOperator::ShiftLeftAssign => {
299 let val = r.read()?.op_shl(&rhs, ctx.stage)?;
300 r.write(val)?;
301 }
302 }
303 Ok(Flow::Next)
304 } else {
305 Err(E::NotRef(lhs))
306 }
307 }
308}
309
310impl Exec for IncrementStatement {
311 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
312 let expr = self.expression.eval(ctx)?;
313 if let Instance::Ref(r) = expr {
314 let mut r = r.read_write()?;
315 match &*r {
316 Instance::Literal(LiteralInstance::I32(n)) => {
317 let val = n.checked_add(1).ok_or(E::IncrOverflow)?;
318 let _ = r.write(LiteralInstance::I32(val).into());
319 Ok(Flow::Next)
320 }
321 Instance::Literal(LiteralInstance::U32(n)) => {
322 let val = n.checked_add(1).ok_or(E::IncrOverflow)?;
323 let _ = r.write(LiteralInstance::U32(val).into());
324 Ok(Flow::Next)
325 }
326 i => Err(E::IncrType(i.ty())),
327 }
328 } else {
329 Err(E::NotRef(expr))
330 }
331 }
332}
333
334impl Exec for DecrementStatement {
335 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
336 let expr = self.expression.eval(ctx)?;
337 if let Instance::Ref(r) = expr {
338 let mut r = r.read_write()?;
339 match &*r {
340 Instance::Literal(LiteralInstance::I32(n)) => {
341 let val = n.checked_sub(1).ok_or(E::DecrOverflow)?;
342 let _ = r.write(LiteralInstance::I32(val).into());
343 Ok(Flow::Next)
344 }
345 Instance::Literal(LiteralInstance::U32(n)) => {
346 let val = n.checked_sub(1).ok_or(E::DecrOverflow)?;
347 let _ = r.write(LiteralInstance::U32(val).into());
348 Ok(Flow::Next)
349 }
350 r => Err(E::DecrType(r.ty())),
351 }
352 } else {
353 Err(E::NotRef(expr))
354 }
355 }
356}
357
358impl Exec for IfStatement {
359 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
360 {
361 let expr = self.if_clause.expression.eval_value(ctx)?;
362 let cond = match expr {
363 Instance::Literal(LiteralInstance::Bool(b)) => Ok(b),
364 _ => Err(E::Type(Type::Bool, expr.ty())),
365 }?;
366
367 if cond {
368 let flow = self.if_clause.body.exec(ctx)?;
369 return Ok(flow);
370 }
371 }
372
373 for elif in &self.else_if_clauses {
374 let expr = elif.expression.eval_value(ctx)?;
375 let cond = match expr {
376 Instance::Literal(LiteralInstance::Bool(b)) => Ok(b),
377 _ => Err(E::Type(Type::Bool, expr.ty())),
378 }?;
379 if cond {
380 let flow = elif.body.exec(ctx)?;
381 return Ok(flow);
382 }
383 }
384
385 if let Some(el) = &self.else_clause {
386 let flow = el.body.exec(ctx)?;
387 return Ok(flow);
388 }
389
390 Ok(Flow::Next)
391 }
392}
393
394impl Exec for SwitchStatement {
395 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
396 let expr = self.expression.eval_value(ctx)?;
397 let ty = expr.ty();
398
399 for clause in &self.clauses {
400 for selector in &clause.case_selectors {
401 match selector {
402 CaseSelector::Default => {
403 let flow = clause.body.exec(ctx)?;
404 if flow == Flow::Break {
405 return Ok(Flow::Next);
406 } else {
407 return Ok(flow);
408 }
409 }
410 CaseSelector::Expression(e) => {
411 let e = with_stage!(ctx, ShaderStage::Const, { e.eval_value(ctx) })?;
412 let e = e
413 .convert_to(&ty)
414 .ok_or_else(|| E::Conversion(e.ty(), ty.clone()))?;
415 if e == expr {
416 let flow = clause.body.exec(ctx)?;
417 if flow == Flow::Break {
418 return Ok(Flow::Next);
419 } else {
420 return Ok(flow);
421 }
422 }
423 }
424 }
425 }
426 }
427
428 Ok(Flow::Next)
429 }
430}
431
432impl Exec for LoopStatement {
433 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
434 loop {
435 let flow = with_scope!(ctx, {
436 let flow = compound_exec(&self.body, ctx, CompoundScope::Leaking)?;
439 match flow {
440 Flow::Next | Flow::Continue => {
441 if let Some(cont) = &self.continuing {
442 cont.exec(ctx) } else {
444 Ok(Flow::Next)
445 }
446 }
447 Flow::Break | Flow::Return(_) => Ok(flow),
448 }
449 })?;
450
451 match flow {
453 Flow::Next | Flow::Continue => (),
454 Flow::Break => return Ok(Flow::Next),
455 Flow::Return(_) => return Ok(flow),
456 }
457 }
458 }
459}
460
461impl Exec for ContinuingStatement {
462 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
463 with_scope!(ctx, {
464 let flow = compound_exec(&self.body, ctx, CompoundScope::Leaking)?;
467 match flow {
468 Flow::Next => {
469 if let Some(break_if) = &self.break_if {
470 let expr = break_if.expression.eval_value(ctx)?;
471 let cond = match expr {
472 Instance::Literal(LiteralInstance::Bool(b)) => Ok(b),
473 _ => Err(E::Type(Type::Bool, expr.ty())),
474 }?;
475 if cond {
476 Ok(Flow::Break)
477 } else {
478 Ok(Flow::Next)
479 }
480 } else {
481 Ok(Flow::Next)
482 }
483 }
484 Flow::Break | Flow::Continue | Flow::Return(_) => Err(E::FlowInContinuing(flow)),
485 }
486 })
487 }
488}
489
490impl Exec for ForStatement {
491 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
492 with_scope!(ctx, {
495 if let Some(init) = &self.initializer {
496 let flow = init.exec(ctx)?;
497 if flow != Flow::Next {
498 return Ok(flow);
499 }
500 }
501
502 loop {
503 let cond = self
504 .condition
505 .as_ref()
506 .map(|expr| {
507 let expr = expr.eval_value(ctx)?;
508 match expr {
509 Instance::Literal(LiteralInstance::Bool(b)) => Ok(b),
510 _ => Err(E::Type(Type::Bool, expr.ty())),
511 }
512 })
513 .unwrap_or(Ok(false))?;
514
515 if !cond {
516 break;
517 }
518
519 let flow = compound_exec(&self.body, ctx, CompoundScope::Transparent)?;
521
522 match flow {
523 Flow::Next | Flow::Continue => {
524 if let Some(updt) = &self.update {
525 updt.exec(ctx)?;
526 }
527 }
528 Flow::Break => {
529 break;
530 }
531 Flow::Return(_) => {
532 return Ok(flow);
533 }
534 }
535 }
536
537 Ok(Flow::Next)
538 })
539 }
540}
541
542impl Exec for WhileStatement {
543 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
544 loop {
545 let expr = self.condition.eval_value(ctx)?;
546 let cond = match expr {
547 Instance::Literal(LiteralInstance::Bool(b)) => Ok(b),
548 _ => Err(E::Type(Type::Bool, expr.ty())),
549 }?;
550
551 if cond {
552 let flow = self.body.exec(ctx)?;
553 match flow {
554 Flow::Next | Flow::Continue => (),
555 Flow::Break => return Ok(Flow::Next),
556 Flow::Return(_) => return Ok(flow),
557 }
558 } else {
559 return Ok(Flow::Next);
560 }
561 }
562 }
563}
564
565impl Exec for BreakStatement {
566 fn exec(&self, _ctx: &mut Context) -> Result<Flow, E> {
567 Ok(Flow::Break)
568 }
569}
570
571impl Exec for ContinueStatement {
572 fn exec(&self, _ctx: &mut Context) -> Result<Flow, E> {
573 Ok(Flow::Continue)
574 }
575}
576
577impl Exec for ReturnStatement {
578 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
579 if let Some(e) = &self.expression {
580 let inst = e.eval_value(ctx)?;
581 Ok(Flow::Return(Some(inst)))
582 } else {
583 Ok(Flow::Return(None))
584 }
585 }
586}
587
588impl Exec for DiscardStatement {
589 fn exec(&self, _ctx: &mut Context) -> Result<Flow, E> {
590 Err(E::DiscardInConst)
591 }
592}
593
594impl Exec for FunctionCallStatement {
595 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
596 let ty = ctx.source.resolve_ty(&self.call.ty);
597 let fn_name = ty.ident.to_string();
598
599 let is_must_use = match ctx.source.decl(&fn_name) {
600 Some(GlobalDeclaration::Function(decl)) => decl.contains_attribute(&Attribute::MustUse),
601 Some(GlobalDeclaration::Struct(_)) => true,
602 Some(_) => return Err(E::NotCallable(fn_name)),
603 None => {
604 if is_ctor(&fn_name) {
605 true
606 } else {
607 return Err(E::UnknownFunction(fn_name));
608 }
609 }
610 };
611
612 if is_must_use {
613 return Err(E::MustUse(fn_name));
614 }
615
616 self.call.exec(ctx).map(|_| Flow::Next)
617 }
618}
619
620fn exec_fn(
621 decl: &Function,
622 tplt: Option<Vec<TpltParam>>,
623 args: Vec<Instance>,
624 ctx: &mut Context,
625) -> Result<Option<Instance>, E> {
626 let fn_name = decl.ident.to_string();
627
628 if ctx.stage == ShaderStage::Const && !decl.contains_attribute(&Attribute::Const) {
629 return Err(E::NotConst(decl.ident.to_string()));
630 }
631
632 if decl.body.contains_attribute(&ATTR_INTRINSIC) {
633 let call_res = call_builtin_fn(&fn_name, tplt.as_deref(), &args, ctx.stage)?;
634 return Ok(call_res);
635 }
636
637 if args.len() != decl.parameters.len() {
638 return Err(E::ParamCount(
639 decl.ident.to_string(),
640 decl.parameters.len(),
641 args.len(),
642 ));
643 }
644
645 let ret_ty = decl
646 .return_type
647 .as_ref()
648 .map(|expr| ty_eval_ty(expr, ctx))
649 .transpose()?;
650
651 let flow = with_scope!(ctx, {
652 let args = args
653 .iter()
654 .zip(&decl.parameters)
655 .map(|(arg, param)| {
656 let param_ty = ty_eval_ty(¶m.ty, ctx)?;
657 arg.convert_to(¶m_ty)
658 .ok_or_else(|| E::ParamType(param_ty.clone(), arg.ty()))
659 })
660 .collect::<Result<Vec<_>, _>>()
661 .inspect_err(|_| ctx.set_err_decl_ctx(fn_name.clone()))?;
662
663 for (a, p) in zip(args, &decl.parameters) {
664 if !ctx.scope.add(p.ident.to_string(), a) {
665 return Err(E::DuplicateDecl(p.ident.to_string()));
666 }
667 }
668
669 let flow = function_scope!(ctx, {
670 compound_exec(&decl.body, ctx, CompoundScope::Transparent)
672 .inspect_err(|_| ctx.set_err_decl_ctx(fn_name.clone()))
673 })?;
674
675 Ok(flow)
676 })?;
677
678 match (flow, ret_ty) {
679 (flow @ (Flow::Break | Flow::Continue), _) => Err(E::FlowInFunction(flow)),
680 (Flow::Return(Some(inst)), Some(ret_ty)) => inst
681 .convert_to(&ret_ty)
682 .ok_or(E::ReturnType(inst.ty(), fn_name.clone(), ret_ty))
683 .map(Into::into)
684 .inspect_err(|_| ctx.set_err_decl_ctx(fn_name)),
685 (Flow::Return(Some(inst)), None) => Err(E::UnexpectedReturn(fn_name, inst.ty())),
686 (Flow::Next | Flow::Return(None), Some(ret_ty)) => Err(E::NoReturn(fn_name, ret_ty)),
687 (Flow::Next | Flow::Return(None), None) => Ok(None),
688 }
689}
690
691impl Exec for FunctionCall {
692 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
693 let ty = ctx.source.resolve_ty(&self.ty);
694 let fn_name = ty.ident.to_string();
695
696 let tplt = ty
697 .template_args
698 .as_ref()
699 .map(|t| {
700 t.iter()
701 .map(|arg| eval_tplt_arg(arg, ctx))
702 .collect::<Result<Vec<_>, _>>()
703 })
704 .transpose()?;
705
706 let args = self
707 .arguments
708 .iter()
709 .map(|a| a.eval_value(ctx))
710 .collect::<Result<Vec<_>, _>>()?;
711
712 if let Some(decl) = ctx.source.decl(&fn_name) {
713 if let GlobalDeclaration::Function(decl) = decl {
714 exec_fn(decl, tplt, args, ctx).map(Flow::Return)
715 } else if let GlobalDeclaration::Struct(decl) = decl {
716 let struct_ty = *decl.eval_ty(ctx)?.unwrap_struct();
717 let inst = struct_ctor(&struct_ty, &args)?;
718 Ok(Flow::Return(Some(Instance::from(inst))))
719 } else {
720 Err(E::NotCallable(fn_name))
721 }
722 } else if is_ctor(&fn_name) {
723 let call_res = call_builtin_fn(&fn_name, tplt.as_deref(), &args, ctx.stage)?;
724 Ok(Flow::Return(call_res))
725 } else {
726 Err(E::UnknownFunction(fn_name))
727 }
728 }
729}
730
731#[derive(Debug, Clone, Default)]
735pub struct Inputs {
736 pub vertex_index: Option<u32>,
737 pub instance_index: Option<u32>,
738 pub position: Option<[f32; 4]>,
739 pub front_facing: Option<bool>,
740 pub sample_index: Option<u32>,
741 pub sample_mask: Option<u32>,
742 pub local_invocation_id: Option<[u32; 3]>,
743 pub local_invocation_index: Option<u32>,
744 pub global_invocation_id: Option<[u32; 3]>,
745 pub workgroup_id: Option<[u32; 3]>,
746 pub num_workgroups: Option<[u32; 3]>,
747 pub subgroup_invocation_id: Option<u32>,
749 pub subgroup_size: Option<u32>,
751 #[cfg(feature = "naga-ext")]
752 pub subgroup_id: Option<u32>,
753 #[cfg(feature = "naga-ext")]
754 pub num_subgroups: Option<u32>,
755 #[cfg(feature = "naga-ext")]
756 pub primitive_index: Option<u32>,
757 #[cfg(feature = "naga-ext")]
758 pub barycentric: Option<[f32; 3]>,
759 #[cfg(feature = "naga-ext")]
760 pub barycentric_no_perspective: Option<[f32; 3]>,
761 #[cfg(feature = "naga-ext")]
762 pub view_index: Option<u32>,
763
764 #[cfg(feature = "naga-ext")]
766 pub ray_invocation_id: Option<[u32; 3]>,
767 #[cfg(feature = "naga-ext")]
768 pub num_ray_invocations: Option<[u32; 3]>,
769 #[cfg(feature = "naga-ext")]
770 pub instance_custom_data: Option<u32>,
771 #[cfg(feature = "naga-ext")]
772 pub geometry_index: Option<u32>,
773 #[cfg(feature = "naga-ext")]
774 pub world_ray_origin: Option<[f32; 3]>,
775 #[cfg(feature = "naga-ext")]
776 pub world_ray_direction: Option<[f32; 3]>,
777 #[cfg(feature = "naga-ext")]
778 pub object_ray_origin: Option<[f32; 3]>,
779 #[cfg(feature = "naga-ext")]
780 pub object_ray_direction: Option<[f32; 3]>,
781 #[cfg(feature = "naga-ext")]
782 pub ray_t_min: Option<f32>,
783 #[cfg(feature = "naga-ext")]
784 pub ray_t_current_max: Option<f32>,
785 #[cfg(feature = "naga-ext")]
787 pub object_to_world: Option<[[f32; 3]; 4]>,
788 #[cfg(feature = "naga-ext")]
790 pub world_to_object: Option<[[f32; 3]; 4]>,
791 #[cfg(feature = "naga-ext")]
792 pub hit_kind: Option<u32>,
793
794 pub user_defined: HashMap<u32, Instance>,
795}
796
797impl Inputs {
798 pub fn new_zero_initialized() -> Self {
799 Self {
800 vertex_index: Some(0),
801 instance_index: Some(0),
802 position: Some([0.0, 0.0, 0.0, 0.0]),
803 front_facing: Some(true),
804 sample_index: Some(0),
805 sample_mask: Some(0),
806 local_invocation_id: Some([0, 0, 0]),
807 local_invocation_index: Some(0),
808 global_invocation_id: Some([0, 0, 0]),
809 workgroup_id: Some([0, 0, 0]),
810 num_workgroups: Some([1, 1, 1]),
811 subgroup_invocation_id: Some(0),
812 subgroup_size: Some(4),
813 #[cfg(feature = "naga-ext")]
814 subgroup_id: Some(0),
815 #[cfg(feature = "naga-ext")]
816 num_subgroups: Some(1),
817 #[cfg(feature = "naga-ext")]
818 primitive_index: Some(0),
819 #[cfg(feature = "naga-ext")]
820 barycentric: Some([0.0, 0.0, 0.0]),
821 #[cfg(feature = "naga-ext")]
822 barycentric_no_perspective: Some([0.0, 0.0, 0.0]),
823 #[cfg(feature = "naga-ext")]
824 view_index: Some(0),
825 #[cfg(feature = "naga-ext")]
826 ray_invocation_id: Some([0, 0, 0]),
827 #[cfg(feature = "naga-ext")]
828 num_ray_invocations: Some([1, 1, 1]),
829 #[cfg(feature = "naga-ext")]
830 instance_custom_data: Some(0),
831 #[cfg(feature = "naga-ext")]
832 geometry_index: Some(0),
833 #[cfg(feature = "naga-ext")]
834 world_ray_origin: Some([0.0, 0.0, 0.0]),
835 #[cfg(feature = "naga-ext")]
836 world_ray_direction: Some([0.0, 0.0, 0.0]),
837 #[cfg(feature = "naga-ext")]
838 object_ray_origin: Some([0.0, 0.0, 0.0]),
839 #[cfg(feature = "naga-ext")]
840 object_ray_direction: Some([0.0, 0.0, 0.0]),
841 #[cfg(feature = "naga-ext")]
842 ray_t_min: Some(0.0),
843 #[cfg(feature = "naga-ext")]
844 ray_t_current_max: Some(0.0),
845 #[cfg(feature = "naga-ext")]
846 object_to_world: Some([[0.0; 3]; 4]),
847 #[cfg(feature = "naga-ext")]
848 world_to_object: Some([[0.0; 3]; 4]),
849 #[cfg(feature = "naga-ext")]
850 hit_kind: Some(0),
851 user_defined: Default::default(),
852 }
853 }
854}
855
856#[cfg(feature = "naga-ext")]
858fn mat4x3(cols: [[f32; 3]; 4]) -> Instance {
859 wgsl_types::inst::MatInstance::from_cols(cols.map(|col| VecInstance::from(col).into()).to_vec())
860 .into()
861}
862
863pub fn exec_entrypoint(
864 entrypoint: &Function,
865 inputs: Inputs,
866 ctx: &mut Context,
867) -> Result<Option<Instance>, E> {
868 let fn_name = entrypoint.ident.to_string();
869
870 let is_entrypoint = entrypoint
871 .attributes
872 .iter()
873 .any(|attr| attr.node().is_entry_point());
874 if !is_entrypoint {
875 return Err(E::NotEntrypoint(fn_name));
876 }
877
878 let args = entrypoint
879 .parameters
880 .iter()
881 .map(|p| {
882 let param_ty = ty_eval_ty(&p.ty, ctx)?;
883 let inst = if let Some(builtin) = p.attr_builtin() {
884 match builtin {
886 BuiltinValue::VertexIndex => inputs.vertex_index.map(Instance::from),
887 BuiltinValue::InstanceIndex => inputs.instance_index.map(Instance::from),
888 BuiltinValue::Position => inputs.position.map(|v| VecInstance::from(v).into()),
889 BuiltinValue::FrontFacing => inputs.front_facing.map(Instance::from),
890 BuiltinValue::SampleIndex => inputs.sample_index.map(Instance::from),
891 BuiltinValue::SampleMask => inputs.sample_mask.map(Instance::from),
892 BuiltinValue::LocalInvocationId => inputs
893 .local_invocation_id
894 .map(|v| VecInstance::from(v).into()),
895 BuiltinValue::LocalInvocationIndex => {
896 inputs.local_invocation_index.map(Instance::from)
897 }
898 BuiltinValue::GlobalInvocationId => inputs
899 .global_invocation_id
900 .map(|v| VecInstance::from(v).into()),
901 BuiltinValue::WorkgroupId => {
902 inputs.workgroup_id.map(|v| VecInstance::from(v).into())
903 }
904 BuiltinValue::NumWorkgroups => {
905 inputs.num_workgroups.map(|v| VecInstance::from(v).into())
906 }
907 BuiltinValue::SubgroupInvocationId => {
908 inputs.subgroup_invocation_id.map(Instance::from)
909 }
910 BuiltinValue::SubgroupSize => inputs.subgroup_size.map(Instance::from),
911 #[cfg(feature = "naga-ext")]
912 BuiltinValue::SubgroupId => inputs.subgroup_id.map(Instance::from),
913 #[cfg(feature = "naga-ext")]
914 BuiltinValue::NumSubgroups => inputs.num_subgroups.map(Instance::from),
915 #[cfg(feature = "naga-ext")]
916 BuiltinValue::PrimitiveIndex => inputs.primitive_index.map(Instance::from),
917 #[cfg(feature = "naga-ext")]
918 BuiltinValue::Barycentric => {
919 inputs.barycentric.map(|v| VecInstance::from(v).into())
920 }
921 #[cfg(feature = "naga-ext")]
922 BuiltinValue::BarycentricNoPerspective => inputs
923 .barycentric_no_perspective
924 .map(|v| VecInstance::from(v).into()),
925 #[cfg(feature = "naga-ext")]
926 BuiltinValue::ViewIndex => inputs.view_index.map(Instance::from),
927 #[cfg(feature = "naga-ext")]
928 BuiltinValue::RayInvocationId => inputs
929 .ray_invocation_id
930 .map(|v| VecInstance::from(v).into()),
931 #[cfg(feature = "naga-ext")]
932 BuiltinValue::NumRayInvocations => inputs
933 .num_ray_invocations
934 .map(|v| VecInstance::from(v).into()),
935 #[cfg(feature = "naga-ext")]
936 BuiltinValue::InstanceCustomData => {
937 inputs.instance_custom_data.map(Instance::from)
938 }
939 #[cfg(feature = "naga-ext")]
940 BuiltinValue::GeometryIndex => inputs.geometry_index.map(Instance::from),
941 #[cfg(feature = "naga-ext")]
942 BuiltinValue::WorldRayOrigin => {
943 inputs.world_ray_origin.map(|v| VecInstance::from(v).into())
944 }
945 #[cfg(feature = "naga-ext")]
946 BuiltinValue::WorldRayDirection => inputs
947 .world_ray_direction
948 .map(|v| VecInstance::from(v).into()),
949 #[cfg(feature = "naga-ext")]
950 BuiltinValue::ObjectRayOrigin => inputs
951 .object_ray_origin
952 .map(|v| VecInstance::from(v).into()),
953 #[cfg(feature = "naga-ext")]
954 BuiltinValue::ObjectRayDirection => inputs
955 .object_ray_direction
956 .map(|v| VecInstance::from(v).into()),
957 #[cfg(feature = "naga-ext")]
958 BuiltinValue::RayTMin => inputs.ray_t_min.map(Instance::from),
959 #[cfg(feature = "naga-ext")]
960 BuiltinValue::RayTCurrentMax => inputs.ray_t_current_max.map(Instance::from),
961 #[cfg(feature = "naga-ext")]
962 BuiltinValue::ObjectToWorld => inputs.object_to_world.map(mat4x3),
963 #[cfg(feature = "naga-ext")]
964 BuiltinValue::WorldToObject => inputs.world_to_object.map(mat4x3),
965 #[cfg(feature = "naga-ext")]
966 BuiltinValue::HitKind => inputs.hit_kind.map(Instance::from),
967 BuiltinValue::ClipDistances | BuiltinValue::FragDepth => {
968 return Err(E::OutputBuiltin(builtin));
969 }
970 #[cfg(feature = "naga-ext")]
971 BuiltinValue::MeshTaskSize
972 | BuiltinValue::Vertices
973 | BuiltinValue::Primitives
974 | BuiltinValue::VertexCount
975 | BuiltinValue::PrimitiveCount
976 | BuiltinValue::TriangleIndices
977 | BuiltinValue::CullPrimitive => {
978 return Err(E::OutputBuiltin(builtin));
979 }
980 }
981 .ok_or_else(|| E::MissingBuiltinInput(builtin, p.ident.to_string()))
982 } else if let Some(location) = p.attr_location(ctx)? {
983 let inst = inputs
984 .user_defined
985 .get(&location)
986 .ok_or_else(|| E::MissingUserInput(p.ident.to_string(), location))?
987 .clone();
988 Ok(inst)
989 } else {
990 Err(E::InvalidEntrypointParam(p.ident.to_string()))
992 }?;
993
994 if inst.ty() != param_ty {
995 Err(E::ParamType(param_ty, inst.ty()))
996 } else {
997 Ok(inst)
998 }
999 })
1000 .collect::<Result<Vec<_>, _>>()
1001 .inspect_err(|_| ctx.set_err_decl_ctx(fn_name.clone()))?;
1002
1003 let ret_ty = entrypoint
1004 .return_type
1005 .as_ref()
1006 .map(|expr| ty_eval_ty(expr, ctx))
1007 .transpose()?;
1008
1009 let flow = with_scope!(ctx, {
1010 for (a, p) in zip(args, &entrypoint.parameters) {
1011 if !ctx.scope.add(p.ident.to_string(), a) {
1012 return Err(E::DuplicateDecl(p.ident.to_string()));
1013 }
1014 }
1015
1016 let flow = compound_exec(&entrypoint.body, ctx, CompoundScope::Transparent)
1018 .inspect_err(|_| ctx.set_err_decl_ctx(fn_name.clone()))?;
1019
1020 Ok(flow)
1021 })?;
1022
1023 match (flow, ret_ty) {
1024 (flow @ (Flow::Break | Flow::Continue), _) => Err(E::FlowInFunction(flow)),
1025 (Flow::Return(Some(inst)), Some(ret_ty)) => inst
1026 .convert_to(&ret_ty)
1027 .ok_or(E::ReturnType(inst.ty(), fn_name.clone(), ret_ty))
1028 .map(Some)
1029 .inspect_err(|_| ctx.set_err_decl_ctx(fn_name)),
1030 (Flow::Return(Some(inst)), None) => Err(E::UnexpectedReturn(fn_name, inst.ty())),
1031 (Flow::Next | Flow::Return(None), Some(ret_ty)) => Err(E::NoReturn(fn_name, ret_ty)),
1032 (Flow::Next | Flow::Return(None), None) => Ok(None),
1033 }
1034}
1035
1036impl Exec for ConstAssertStatement {
1037 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
1038 with_stage!(ctx, ShaderStage::Const, {
1039 let expr = self.expression.eval_value(ctx)?;
1040 let cond = match expr {
1041 Instance::Literal(LiteralInstance::Bool(b)) => Ok(b),
1042 _ => Err(E::Type(Type::Bool, expr.ty())),
1043 }?;
1044
1045 if cond {
1046 Ok(Flow::Next)
1047 } else {
1048 Err(E::ConstAssertFailure(self.expression.clone()))
1049 }
1050 })
1051 }
1052}
1053
1054impl Exec for Declaration {
1055 fn exec(&self, ctx: &mut Context) -> Result<Flow, E> {
1056 if ctx.scope.local_contains(&self.ident.name()) {
1057 return Err(E::DuplicateDecl(self.ident.to_string()));
1058 }
1059
1060 let ty = match (&self.ty, &self.initializer) {
1061 (None, None) => return Err(E::UntypedDecl),
1062 (None, Some(init)) => {
1063 let ty = init.eval_ty(ctx)?.loaded();
1064 if self.kind.is_const() {
1065 ty } else {
1067 ty.concretize()
1068 }
1069 }
1070 (Some(ty), _) => ty_eval_ty(ty, ctx)?,
1071 };
1072
1073 let init = |ctx: &mut Context, stage: ShaderStage| {
1074 self.initializer
1075 .as_ref()
1076 .map(|init| {
1077 let inst = with_stage!(ctx, stage, { init.eval_value(ctx) })?;
1078 inst.convert_to(&ty)
1079 .ok_or_else(|| E::Conversion(inst.ty(), ty.clone()))
1080 })
1081 .transpose()
1082 };
1083
1084 let inst = match (self.kind, ctx.kind) {
1085 (DeclarationKind::Const, _) => init(ctx, ShaderStage::Const)?
1086 .ok_or_else(|| E::UninitConst(self.ident.to_string()))?,
1087 (DeclarationKind::Override, ScopeKind::Function) => return Err(E::OverrideInFn),
1088 (DeclarationKind::Let, ScopeKind::Function) => {
1089 init(ctx, ctx.stage)?.ok_or_else(|| E::UninitLet(self.ident.to_string()))?
1090 }
1091 (DeclarationKind::Var(a_s), ScopeKind::Function) => {
1092 if !matches!(a_s, Some((AddressSpace::Function, None)) | None) {
1093 return Err(E::ForbiddenDecl(self.kind, ctx.kind));
1094 }
1095 let inst = init(ctx, ctx.stage)?
1096 .map(Ok)
1097 .unwrap_or_else(|| Instance::zero_value(&ty))?;
1098
1099 RefInstance::new(inst, AddressSpace::Function, AccessMode::ReadWrite).into()
1100 }
1101 (DeclarationKind::Override, ScopeKind::Module) => {
1102 if ctx.stage == ShaderStage::Const {
1103 Instance::Deferred(ty)
1104 } else if let Some(inst) = ctx.overridable(&self.ident.name()) {
1105 inst.convert_to(&ty)
1106 .ok_or_else(|| E::Conversion(inst.ty(), ty))?
1107 } else if let Some(inst) = init(ctx, ShaderStage::Override)? {
1108 inst
1109 } else {
1110 return Err(E::UninitOverride(self.ident.to_string()));
1111 }
1112 }
1113 (DeclarationKind::Let, ScopeKind::Module) => return Err(E::LetInMod),
1114 (DeclarationKind::Var(as_am), ScopeKind::Module) => {
1115 let (a_s, a_m) = match as_am {
1116 Some((a_s, Some(a_m))) => (a_s, a_m),
1117 Some((a_s, None)) => (a_s, a_s.default_access_mode()),
1118 None => (AddressSpace::Handle, AccessMode::Read),
1119 };
1120 if ctx.stage == ShaderStage::Const && ctx.kind == ScopeKind::Module {
1121 Instance::Deferred(Type::Ref(a_s, Box::new(ty), a_m))
1122 } else {
1123 match a_s {
1124 AddressSpace::Function => {
1125 return Err(E::ForbiddenDecl(self.kind, ctx.kind));
1126 }
1127 AddressSpace::Private => {
1128 let inst = if let Some(inst) = init(ctx, ShaderStage::Override)? {
1130 inst
1131 } else {
1132 Instance::zero_value(&ty)?
1133 };
1134
1135 RefInstance::new(inst, a_s, a_m).into()
1136 }
1137 AddressSpace::Uniform => {
1138 if self.initializer.is_some() {
1139 return Err(E::ForbiddenInitializer(a_s));
1140 }
1141 let (group, binding) = self.attr_group_binding(ctx)?;
1142 let inst = ctx
1143 .resource(group, binding)
1144 .ok_or(E::MissingResource(group, binding))?;
1145 if inst.ty != ty {
1146 return Err(E::Type(ty, inst.ty.clone()));
1147 }
1148 if inst.space != AddressSpace::Uniform {
1149 return Err(E::AddressSpace(a_s, inst.space));
1150 }
1151 if inst.access != AccessMode::Read {
1152 return Err(E::AccessMode(AccessMode::Read, inst.access));
1153 }
1154 inst.clone().into()
1155 }
1156 AddressSpace::Storage => {
1157 if self.initializer.is_some() {
1158 return Err(E::ForbiddenInitializer(a_s));
1159 }
1160 let Some(ty) = &self.ty else {
1161 return Err(E::UntypedDecl);
1162 };
1163 let ty = ty_eval_ty(ty, ctx)?;
1164 let (group, binding) = self.attr_group_binding(ctx)?;
1165 let inst = ctx
1166 .resource(group, binding)
1167 .ok_or(E::MissingResource(group, binding))?;
1168 if ty != inst.ty {
1169 return Err(E::Type(ty, inst.ty.clone()));
1170 }
1171 if inst.space != AddressSpace::Storage {
1172 return Err(E::AddressSpace(a_s, inst.space));
1173 }
1174 if inst.access != a_m {
1175 return Err(E::AccessMode(a_m, inst.access));
1176 }
1177 inst.clone().into()
1178 }
1179 AddressSpace::Workgroup => {
1180 if self.initializer.is_some() {
1181 return Err(E::ForbiddenInitializer(a_s));
1182 }
1183
1184 let inst = Instance::zero_value(&ty)?;
1187
1188 RefInstance::new(inst, a_s, a_m).into()
1189 }
1190 AddressSpace::Handle => todo!("handle address space"),
1191 AddressSpace::Immediate => todo!("immediate address space"),
1192 #[cfg(feature = "naga-ext")]
1193 AddressSpace::TaskPayload => todo!("task_payload address space"),
1194 #[cfg(feature = "naga-ext")]
1195 AddressSpace::RayPayload => todo!("ray_payload address space"),
1196 #[cfg(feature = "naga-ext")]
1197 AddressSpace::IncomingRayPayload => {
1198 todo!("incoming_ray_payload address space")
1199 }
1200 }
1201 }
1202 }
1203 };
1204
1205 if ctx.scope.add(self.ident.to_string(), inst) {
1206 Ok(Flow::Next)
1207 } else {
1208 Err(E::DuplicateDecl(self.ident.to_string()))
1209 }
1210 }
1211}