From 6a61e62f55edbfd0c013e79fc1981719a17433d9 Mon Sep 17 00:00:00 2001 From: Jamie Nicol Date: Fri, 7 Mar 2025 03:50:36 +0000 Subject: [PATCH] [naga wgsl-in] Handle automatic type conversions for switch selector and case expressions (#7250) This allows abstract-typed expressions to be used for some or all of the switch selector and case selectors. If these are all not convertible to the same concrete scalar integer type we return an error. If all the selector expressions are abstract then they are concretized to i32. The note previously provided by the relevant error message, suggesting adding or removing the `u` suffix from case values, has been removed. While useful for simple literal values, it was comically incorrect for more complex case expressions. The error message should still be useful enough to allow the user to easily identify the problem. --- CHANGELOG.md | 1 + naga/src/front/wgsl/error.rs | 47 +- naga/src/front/wgsl/lower/mod.rs | 64 +- naga/src/front/wgsl/tests.rs | 6 +- naga/src/proc/mod.rs | 2 +- naga/tests/in/wgsl/control-flow.wgsl | 35 + .../out/glsl/control-flow.main.Compute.glsl | 42 ++ naga/tests/out/hlsl/control-flow.hlsl | 44 ++ naga/tests/out/msl/control-flow.msl | 44 ++ naga/tests/out/spv/control-flow.spvasm | 645 ++++++++++-------- naga/tests/out/wgsl/control-flow.wgsl | 40 ++ naga/tests/wgsl_errors.rs | 102 ++- 12 files changed, 723 insertions(+), 349 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d6c62893b..36ad63c4a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -200,6 +200,7 @@ By @Vecvec in [#6905](https://github.com/gfx-rs/wgpu/pull/6905), [#7086](https:/ - Allow template lists to have a trailing comma. By @KentSlaney in [#7142](https://github.com/gfx-rs/wgpu/pull/7142). - Allow WGSL const declarations to have abstract types. By @jamienicol in [#7055](https://github.com/gfx-rs/wgpu/pull/7055) and [#7222](https://github.com/gfx-rs/wgpu/pull/7222). - Allows override-sized arrays to resolve to the same size without causing the type arena to panic. By @KentSlaney in [#7082](https://github.com/gfx-rs/wgpu/pull/7082). +- Allow abstract types to be used for WGSL switch statement selector and case selector expressions. By @jamienicol in [#7250](https://github.com/gfx-rs/wgpu/pull/7250). #### General diff --git a/naga/src/front/wgsl/error.rs b/naga/src/front/wgsl/error.rs index de45cd2812..6bbf9f476c 100644 --- a/naga/src/front/wgsl/error.rs +++ b/naga/src/front/wgsl/error.rs @@ -256,8 +256,13 @@ pub(crate) enum Error<'a> { /// the same identifier as `ident`, above. path: Box<[(Span, Span)]>, }, - InvalidSwitchValue { - uint: bool, + InvalidSwitchSelector { + span: Span, + }, + InvalidSwitchCase { + span: Span, + }, + SwitchCaseTypeMismatch { span: Span, }, CalledEntryPoint(Span), @@ -772,26 +777,32 @@ impl<'a> Error<'a> { .collect(), notes: vec![], }, - Error::InvalidSwitchValue { uint, span } => ParseError { - message: "invalid switch value".to_string(), + Error::InvalidSwitchSelector { span } => ParseError { + message: "invalid `switch` selector".to_string(), labels: vec![( span, - if uint { - "expected unsigned integer" - } else { - "expected signed integer" - } + "`switch` selector must be a scalar integer" .into(), )], - notes: vec![if uint { - format!("suffix the integer with a `u`: `{}u`", &source[span]) - } else { - let span = span.to_range().unwrap(); - format!( - "remove the `u` suffix: `{}`", - &source[span.start..span.end - 1] - ) - }], + notes: vec![], + }, + Error::InvalidSwitchCase { span } => ParseError { + message: "invalid `switch` case selector value".to_string(), + labels: vec![( + span, + "`switch` case selector must be a scalar integer const expression" + .into(), + )], + notes: vec![], + }, + Error::SwitchCaseTypeMismatch { span } => ParseError { + message: "invalid `switch` case selector value".to_string(), + labels: vec![( + span, + "`switch` case selector must have the same type as the `switch` selector expression" + .into(), + )], + notes: vec![], }, Error::CalledEntryPoint(span) => ParseError { message: "entry point cannot be called".to_string(), diff --git a/naga/src/front/wgsl/lower/mod.rs b/naga/src/front/wgsl/lower/mod.rs index 385a59c5b0..96032f8268 100644 --- a/naga/src/front/wgsl/lower/mod.rs +++ b/naga/src/front/wgsl/lower/mod.rs @@ -1630,12 +1630,53 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { emitter.start(&ctx.function.expressions); let mut ectx = ctx.as_expression(block, &mut emitter); - let selector = self.expression(selector, &mut ectx)?; - let uint = - resolve_inner!(ectx, selector).scalar_kind() == Some(crate::ScalarKind::Uint); + // Determine the scalar type of the selector and case expressions, find the + // consensus type for automatic conversion, then convert them. + let (mut exprs, spans) = core::iter::once(selector) + .chain(cases.iter().filter_map(|case| match case.value { + ast::SwitchValue::Expr(expr) => Some(expr), + ast::SwitchValue::Default => None, + })) + .enumerate() + .map(|(i, expr)| { + let span = ectx.ast_expressions.get_span(expr); + let expr = self.expression_for_abstract(expr, &mut ectx)?; + let ty = resolve_inner!(ectx, expr); + match *ty { + crate::TypeInner::Scalar( + crate::Scalar::I32 + | crate::Scalar::U32 + | crate::Scalar::ABSTRACT_INT, + ) => Ok((expr, span)), + _ => match i { + 0 => Err(Error::InvalidSwitchSelector { span }), + _ => Err(Error::InvalidSwitchCase { span }), + }, + } + }) + .collect::, Vec<_>), _>>()?; + + let mut consensus = + ectx.automatic_conversion_consensus(&exprs) + .map_err(|span_idx| Error::SwitchCaseTypeMismatch { + span: spans[span_idx], + })?; + // Concretize to I32 if the selector and all cases were abstract + if consensus == crate::Scalar::ABSTRACT_INT { + consensus = crate::Scalar::I32; + } + for expr in &mut exprs { + ectx.convert_to_leaf_scalar(expr, consensus)?; + } + block.extend(emitter.finish(&ctx.function.expressions)); + let mut exprs = exprs.into_iter(); + let selector = exprs + .next() + .expect("First element should be selector expression"); + let cases = cases .iter() .map(|case| { @@ -1643,17 +1684,22 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { value: match case.value { ast::SwitchValue::Expr(expr) => { let span = ctx.ast_expressions.get_span(expr); - let expr = - self.expression(expr, &mut ctx.as_global().as_const())?; - match ctx.module.to_ctx().eval_expr_to_literal(expr) { - Some(crate::Literal::I32(value)) if !uint => { + let expr = exprs.next().expect( + "Should yield expression for each SwitchValue::Expr case", + ); + match ctx + .module + .to_ctx() + .eval_expr_to_literal_from(expr, &ctx.function.expressions) + { + Some(crate::Literal::I32(value)) => { crate::SwitchValue::I32(value) } - Some(crate::Literal::U32(value)) if uint => { + Some(crate::Literal::U32(value)) => { crate::SwitchValue::U32(value) } _ => { - return Err(Error::InvalidSwitchValue { uint, span }); + return Err(Error::InvalidSwitchCase { span }); } } } diff --git a/naga/src/front/wgsl/tests.rs b/naga/src/front/wgsl/tests.rs index c0a24fbdf7..a0e1d7a3cc 100644 --- a/naga/src/front/wgsl/tests.rs +++ b/naga/src/front/wgsl/tests.rs @@ -322,9 +322,9 @@ fn parse_parentheses_switch() { parse_str( " fn main() { - var pos: f32; - switch pos > 1.0 { - default: { pos = 3.0; } + var pos: i32; + switch pos + 1 { + default: { pos = 3; } } } ", diff --git a/naga/src/proc/mod.rs b/naga/src/proc/mod.rs index 44c76b911d..84b4d1af42 100644 --- a/naga/src/proc/mod.rs +++ b/naga/src/proc/mod.rs @@ -456,7 +456,7 @@ impl GlobalCtx<'_> { self.eval_expr_to_literal_from(handle, self.global_expressions) } - fn eval_expr_to_literal_from( + pub(super) fn eval_expr_to_literal_from( &self, handle: crate::Handle, arena: &crate::Arena, diff --git a/naga/tests/in/wgsl/control-flow.wgsl b/naga/tests/in/wgsl/control-flow.wgsl index 564f1ab2a5..1d5b45555e 100644 --- a/naga/tests/in/wgsl/control-flow.wgsl +++ b/naga/tests/in/wgsl/control-flow.wgsl @@ -79,6 +79,41 @@ fn switch_case_break() { return; } +fn switch_selector_type_conversion() { + switch (0u) { + case 0: { + } + default: { + } + } + + switch (0) { + case 0u: { + } + default: { + } + } +} + +const ONE = 1; +fn switch_const_expr_case_selectors() { + const TWO = 2; + switch (0) { + case i32(): { + } + case ONE: { + } + case TWO: { + } + case 1 + 2: { + } + case vec4(4).x: { + } + default: { + } + } +} + fn loop_switch_continue(x: i32) { loop { switch x { diff --git a/naga/tests/out/glsl/control-flow.main.Compute.glsl b/naga/tests/out/glsl/control-flow.main.Compute.glsl index 48f1bffa30..edf4093e53 100644 --- a/naga/tests/out/glsl/control-flow.main.Compute.glsl +++ b/naga/tests/out/glsl/control-flow.main.Compute.glsl @@ -24,6 +24,48 @@ void switch_case_break() { return; } +void switch_selector_type_conversion() { + switch(0u) { + case 0u: { + break; + } + default: { + break; + } + } + switch(0u) { + case 0u: { + return; + } + default: { + return; + } + } +} + +void switch_const_expr_case_selectors() { + switch(0) { + case 0: { + return; + } + case 1: { + return; + } + case 2: { + return; + } + case 3: { + return; + } + case 4: { + return; + } + default: { + return; + } + } +} + void loop_switch_continue(int x) { while(true) { switch(x) { diff --git a/naga/tests/out/hlsl/control-flow.hlsl b/naga/tests/out/hlsl/control-flow.hlsl index 19e4c8d2a2..396e6bb2c3 100644 --- a/naga/tests/out/hlsl/control-flow.hlsl +++ b/naga/tests/out/hlsl/control-flow.hlsl @@ -18,6 +18,50 @@ void switch_case_break() return; } +void switch_selector_type_conversion() +{ + switch(0u) { + case 0u: { + break; + } + default: { + break; + } + } + switch(0u) { + case 0u: { + return; + } + default: { + return; + } + } +} + +void switch_const_expr_case_selectors() +{ + switch(int(0)) { + case 0: { + return; + } + case 1: { + return; + } + case 2: { + return; + } + case 3: { + return; + } + case 4: { + return; + } + default: { + return; + } + } +} + void loop_switch_continue(int x) { uint2 loop_bound = uint2(0u, 0u); diff --git a/naga/tests/out/msl/control-flow.msl b/naga/tests/out/msl/control-flow.msl index eecf31fb9b..8c373aa8be 100644 --- a/naga/tests/out/msl/control-flow.msl +++ b/naga/tests/out/msl/control-flow.msl @@ -28,6 +28,50 @@ void switch_case_break( return; } +void switch_selector_type_conversion( +) { + switch(0u) { + case 0u: { + break; + } + default: { + break; + } + } + switch(0u) { + case 0u: { + return; + } + default: { + return; + } + } +} + +void switch_const_expr_case_selectors( +) { + switch(0) { + case 0: { + return; + } + case 1: { + return; + } + case 2: { + return; + } + case 3: { + return; + } + case 4: { + return; + } + default: { + return; + } + } +} + void loop_switch_continue( int x ) { diff --git a/naga/tests/out/spv/control-flow.spvasm b/naga/tests/out/spv/control-flow.spvasm index a2d6dc070e..d9db923910 100644 --- a/naga/tests/out/spv/control-flow.spvasm +++ b/naga/tests/out/spv/control-flow.spvasm @@ -1,13 +1,13 @@ ; SPIR-V ; Version: 1.1 ; Generator: rspirv -; Bound: 208 +; Bound: 227 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %179 "main" %176 -OpExecutionMode %179 LocalSize 1 1 1 -OpDecorate %176 BuiltIn GlobalInvocationId +OpEntryPoint GLCompute %198 "main" %195 +OpExecutionMode %198 LocalSize 1 1 1 +OpDecorate %195 BuiltIn GlobalInvocationId %2 = OpTypeVoid %4 = OpTypeInt 32 0 %3 = OpTypeVector %4 3 @@ -15,29 +15,29 @@ OpDecorate %176 BuiltIn GlobalInvocationId %9 = OpTypeFunction %2 %5 %15 = OpTypeFunction %2 %16 = OpConstant %5 0 -%29 = OpTypeVector %4 2 -%30 = OpTypePointer Function %29 -%31 = OpTypeBool -%32 = OpTypeVector %31 2 -%33 = OpConstant %4 0 -%34 = OpConstantComposite %29 %33 %33 -%35 = OpConstant %4 1 -%36 = OpConstant %4 4294967295 -%37 = OpConstantComposite %29 %36 %36 -%57 = OpTypeFunction %2 %5 %5 %5 -%126 = OpTypeFunction %2 %5 %5 %5 %5 -%127 = OpConstant %5 1 -%128 = OpConstant %5 2 -%130 = OpTypePointer Function %5 -%177 = OpTypePointer Input %3 -%176 = OpVariable %177 Input -%180 = OpConstant %5 3 -%181 = OpConstant %5 4 -%183 = OpConstantNull %5 -%185 = OpConstant %4 2 -%186 = OpConstant %4 72 -%187 = OpConstant %4 264 -%188 = OpConstant %4 2056 +%23 = OpConstant %4 0 +%49 = OpTypeVector %4 2 +%50 = OpTypePointer Function %49 +%51 = OpTypeBool +%52 = OpTypeVector %51 2 +%53 = OpConstantComposite %49 %23 %23 +%54 = OpConstant %4 1 +%55 = OpConstant %4 4294967295 +%56 = OpConstantComposite %49 %55 %55 +%76 = OpTypeFunction %2 %5 %5 %5 +%145 = OpTypeFunction %2 %5 %5 %5 %5 +%146 = OpConstant %5 1 +%147 = OpConstant %5 2 +%149 = OpTypePointer Function %5 +%196 = OpTypePointer Input %3 +%195 = OpVariable %196 Input +%199 = OpConstant %5 3 +%200 = OpConstant %5 4 +%202 = OpConstantNull %5 +%204 = OpConstant %4 2 +%205 = OpConstant %4 72 +%206 = OpConstant %4 264 +%207 = OpConstant %4 2056 %8 = OpFunction %2 None %9 %7 = OpFunctionParameter %5 %6 = OpLabel @@ -63,312 +63,353 @@ OpBranch %18 %18 = OpLabel OpReturn OpFunctionEnd -%23 = OpFunction %2 None %9 -%22 = OpFunctionParameter %5 +%22 = OpFunction %2 None %15 %21 = OpLabel -%38 = OpVariable %30 Function %34 OpBranch %24 %24 = OpLabel +OpSelectionMerge %25 None +OpSwitch %23 %27 0 %26 +%26 = OpLabel +OpBranch %25 +%27 = OpLabel OpBranch %25 %25 = OpLabel -OpLoopMerge %26 %28 None -OpBranch %39 -%39 = OpLabel -%40 = OpLoad %29 %38 -%41 = OpIEqual %32 %37 %40 -%42 = OpAll %31 %41 -OpSelectionMerge %43 None -OpBranchConditional %42 %26 %43 -%43 = OpLabel -%44 = OpCompositeExtract %4 %40 1 -%45 = OpIEqual %31 %44 %36 -%46 = OpSelect %4 %45 %35 %33 -%47 = OpCompositeConstruct %29 %46 %35 -%48 = OpIAdd %29 %40 %47 -OpStore %38 %48 -OpBranch %27 -%27 = OpLabel -OpSelectionMerge %49 None -OpSwitch %22 %51 1 %50 -%50 = OpLabel -OpBranch %28 -%51 = OpLabel -OpBranch %49 -%49 = OpLabel -OpBranch %28 +OpSelectionMerge %28 None +OpSwitch %23 %30 0 %29 +%29 = OpLabel +OpReturn +%30 = OpLabel +OpReturn %28 = OpLabel -OpBranch %25 -%26 = OpLabel OpReturn OpFunctionEnd -%56 = OpFunction %2 None %57 -%53 = OpFunctionParameter %5 -%54 = OpFunctionParameter %5 -%55 = OpFunctionParameter %5 -%52 = OpLabel -%63 = OpVariable %30 Function %34 -%85 = OpVariable %30 Function %34 -%105 = OpVariable %30 Function %34 +%32 = OpFunction %2 None %15 +%31 = OpLabel +OpBranch %33 +%33 = OpLabel +OpSelectionMerge %34 None +OpSwitch %16 %40 0 %35 1 %36 2 %37 3 %38 4 %39 +%35 = OpLabel +OpReturn +%36 = OpLabel +OpReturn +%37 = OpLabel +OpReturn +%38 = OpLabel +OpReturn +%39 = OpLabel +OpReturn +%40 = OpLabel +OpReturn +%34 = OpLabel +OpReturn +OpFunctionEnd +%43 = OpFunction %2 None %9 +%42 = OpFunctionParameter %5 +%41 = OpLabel +%57 = OpVariable %50 Function %53 +OpBranch %44 +%44 = OpLabel +OpBranch %45 +%45 = OpLabel +OpLoopMerge %46 %48 None OpBranch %58 %58 = OpLabel -OpBranch %59 -%59 = OpLabel -OpLoopMerge %60 %62 None -OpBranch %64 -%64 = OpLabel -%65 = OpLoad %29 %63 -%66 = OpIEqual %32 %37 %65 -%67 = OpAll %31 %66 -OpSelectionMerge %68 None -OpBranchConditional %67 %60 %68 -%68 = OpLabel -%69 = OpCompositeExtract %4 %65 1 -%70 = OpIEqual %31 %69 %36 -%71 = OpSelect %4 %70 %35 %33 -%72 = OpCompositeConstruct %29 %71 %35 -%73 = OpIAdd %29 %65 %72 -OpStore %63 %73 -OpBranch %61 -%61 = OpLabel -OpSelectionMerge %74 None -OpSwitch %53 %77 1 %75 2 %76 -%75 = OpLabel -OpBranch %62 -%76 = OpLabel -OpSelectionMerge %78 None -OpSwitch %54 %80 1 %79 -%79 = OpLabel -OpBranch %62 -%80 = OpLabel -OpBranch %81 -%81 = OpLabel -OpLoopMerge %82 %84 None -OpBranch %86 -%86 = OpLabel -%87 = OpLoad %29 %85 -%88 = OpIEqual %32 %37 %87 -%89 = OpAll %31 %88 -OpSelectionMerge %90 None -OpBranchConditional %89 %82 %90 -%90 = OpLabel -%91 = OpCompositeExtract %4 %87 1 -%92 = OpIEqual %31 %91 %36 -%93 = OpSelect %4 %92 %35 %33 -%94 = OpCompositeConstruct %29 %93 %35 -%95 = OpIAdd %29 %87 %94 -OpStore %85 %95 -OpBranch %83 -%83 = OpLabel -OpSelectionMerge %96 None -OpSwitch %55 %98 1 %97 -%97 = OpLabel -OpBranch %84 -%98 = OpLabel -OpBranch %96 -%96 = OpLabel -OpBranch %84 -%84 = OpLabel -OpBranch %81 -%82 = OpLabel -OpBranch %78 -%78 = OpLabel -OpBranch %74 -%77 = OpLabel -OpBranch %74 -%74 = OpLabel -OpSelectionMerge %99 None -OpSwitch %54 %100 -%100 = OpLabel -OpBranch %62 -%99 = OpLabel -OpBranch %62 +%59 = OpLoad %49 %57 +%60 = OpIEqual %52 %56 %59 +%61 = OpAll %51 %60 +OpSelectionMerge %62 None +OpBranchConditional %61 %46 %62 %62 = OpLabel -OpBranch %59 -%60 = OpLabel -OpBranch %101 -%101 = OpLabel -OpLoopMerge %102 %104 None -OpBranch %106 -%106 = OpLabel -%107 = OpLoad %29 %105 -%108 = OpIEqual %32 %37 %107 -%109 = OpAll %31 %108 -OpSelectionMerge %110 None -OpBranchConditional %109 %102 %110 -%110 = OpLabel -%111 = OpCompositeExtract %4 %107 1 -%112 = OpIEqual %31 %111 %36 -%113 = OpSelect %4 %112 %35 %33 -%114 = OpCompositeConstruct %29 %113 %35 -%115 = OpIAdd %29 %107 %114 -OpStore %105 %115 -OpBranch %103 -%103 = OpLabel -OpSelectionMerge %116 None -OpSwitch %54 %117 1 %117 -%117 = OpLabel -OpSelectionMerge %118 None -OpSwitch %55 %119 -%119 = OpLabel -OpBranch %104 -%118 = OpLabel -OpBranch %116 -%116 = OpLabel -OpBranch %104 -%104 = OpLabel -OpBranch %101 -%102 = OpLabel +%63 = OpCompositeExtract %4 %59 1 +%64 = OpIEqual %51 %63 %55 +%65 = OpSelect %4 %64 %54 %23 +%66 = OpCompositeConstruct %49 %65 %54 +%67 = OpIAdd %49 %59 %66 +OpStore %57 %67 +OpBranch %47 +%47 = OpLabel +OpSelectionMerge %68 None +OpSwitch %42 %70 1 %69 +%69 = OpLabel +OpBranch %48 +%70 = OpLabel +OpBranch %68 +%68 = OpLabel +OpBranch %48 +%48 = OpLabel +OpBranch %45 +%46 = OpLabel OpReturn OpFunctionEnd -%125 = OpFunction %2 None %126 -%121 = OpFunctionParameter %5 -%122 = OpFunctionParameter %5 -%123 = OpFunctionParameter %5 -%124 = OpFunctionParameter %5 +%75 = OpFunction %2 None %76 +%72 = OpFunctionParameter %5 +%73 = OpFunctionParameter %5 +%74 = OpFunctionParameter %5 +%71 = OpLabel +%82 = OpVariable %50 Function %53 +%104 = OpVariable %50 Function %53 +%124 = OpVariable %50 Function %53 +OpBranch %77 +%77 = OpLabel +OpBranch %78 +%78 = OpLabel +OpLoopMerge %79 %81 None +OpBranch %83 +%83 = OpLabel +%84 = OpLoad %49 %82 +%85 = OpIEqual %52 %56 %84 +%86 = OpAll %51 %85 +OpSelectionMerge %87 None +OpBranchConditional %86 %79 %87 +%87 = OpLabel +%88 = OpCompositeExtract %4 %84 1 +%89 = OpIEqual %51 %88 %55 +%90 = OpSelect %4 %89 %54 %23 +%91 = OpCompositeConstruct %49 %90 %54 +%92 = OpIAdd %49 %84 %91 +OpStore %82 %92 +OpBranch %80 +%80 = OpLabel +OpSelectionMerge %93 None +OpSwitch %72 %96 1 %94 2 %95 +%94 = OpLabel +OpBranch %81 +%95 = OpLabel +OpSelectionMerge %97 None +OpSwitch %73 %99 1 %98 +%98 = OpLabel +OpBranch %81 +%99 = OpLabel +OpBranch %100 +%100 = OpLabel +OpLoopMerge %101 %103 None +OpBranch %105 +%105 = OpLabel +%106 = OpLoad %49 %104 +%107 = OpIEqual %52 %56 %106 +%108 = OpAll %51 %107 +OpSelectionMerge %109 None +OpBranchConditional %108 %101 %109 +%109 = OpLabel +%110 = OpCompositeExtract %4 %106 1 +%111 = OpIEqual %51 %110 %55 +%112 = OpSelect %4 %111 %54 %23 +%113 = OpCompositeConstruct %49 %112 %54 +%114 = OpIAdd %49 %106 %113 +OpStore %104 %114 +OpBranch %102 +%102 = OpLabel +OpSelectionMerge %115 None +OpSwitch %74 %117 1 %116 +%116 = OpLabel +OpBranch %103 +%117 = OpLabel +OpBranch %115 +%115 = OpLabel +OpBranch %103 +%103 = OpLabel +OpBranch %100 +%101 = OpLabel +OpBranch %97 +%97 = OpLabel +OpBranch %93 +%96 = OpLabel +OpBranch %93 +%93 = OpLabel +OpSelectionMerge %118 None +OpSwitch %73 %119 +%119 = OpLabel +OpBranch %81 +%118 = OpLabel +OpBranch %81 +%81 = OpLabel +OpBranch %78 +%79 = OpLabel +OpBranch %120 %120 = OpLabel -%129 = OpVariable %130 Function %16 -%136 = OpVariable %30 Function %34 -%154 = OpVariable %30 Function %34 -OpBranch %131 -%131 = OpLabel -OpBranch %132 -%132 = OpLabel -OpLoopMerge %133 %135 None -OpBranch %137 +OpLoopMerge %121 %123 None +OpBranch %125 +%125 = OpLabel +%126 = OpLoad %49 %124 +%127 = OpIEqual %52 %56 %126 +%128 = OpAll %51 %127 +OpSelectionMerge %129 None +OpBranchConditional %128 %121 %129 +%129 = OpLabel +%130 = OpCompositeExtract %4 %126 1 +%131 = OpIEqual %51 %130 %55 +%132 = OpSelect %4 %131 %54 %23 +%133 = OpCompositeConstruct %49 %132 %54 +%134 = OpIAdd %49 %126 %133 +OpStore %124 %134 +OpBranch %122 +%122 = OpLabel +OpSelectionMerge %135 None +OpSwitch %73 %136 1 %136 +%136 = OpLabel +OpSelectionMerge %137 None +OpSwitch %74 %138 +%138 = OpLabel +OpBranch %123 %137 = OpLabel -%138 = OpLoad %29 %136 -%139 = OpIEqual %32 %37 %138 -%140 = OpAll %31 %139 -OpSelectionMerge %141 None -OpBranchConditional %140 %133 %141 -%141 = OpLabel -%142 = OpCompositeExtract %4 %138 1 -%143 = OpIEqual %31 %142 %36 -%144 = OpSelect %4 %143 %35 %33 -%145 = OpCompositeConstruct %29 %144 %35 -%146 = OpIAdd %29 %138 %145 -OpStore %136 %146 -OpBranch %134 -%134 = OpLabel -OpSelectionMerge %147 None -OpSwitch %121 %149 1 %148 -%148 = OpLabel -OpStore %129 %127 -OpBranch %147 -%149 = OpLabel -OpBranch %147 -%147 = OpLabel OpBranch %135 %135 = OpLabel -OpBranch %132 -%133 = OpLabel +OpBranch %123 +%123 = OpLabel +OpBranch %120 +%121 = OpLabel +OpReturn +OpFunctionEnd +%144 = OpFunction %2 None %145 +%140 = OpFunctionParameter %5 +%141 = OpFunctionParameter %5 +%142 = OpFunctionParameter %5 +%143 = OpFunctionParameter %5 +%139 = OpLabel +%148 = OpVariable %149 Function %16 +%155 = OpVariable %50 Function %53 +%173 = OpVariable %50 Function %53 OpBranch %150 %150 = OpLabel -OpLoopMerge %151 %153 None -OpBranch %155 -%155 = OpLabel -%156 = OpLoad %29 %154 -%157 = OpIEqual %32 %37 %156 -%158 = OpAll %31 %157 -OpSelectionMerge %159 None -OpBranchConditional %158 %151 %159 -%159 = OpLabel -%160 = OpCompositeExtract %4 %156 1 -%161 = OpIEqual %31 %160 %36 -%162 = OpSelect %4 %161 %35 %33 -%163 = OpCompositeConstruct %29 %162 %35 -%164 = OpIAdd %29 %156 %163 -OpStore %154 %164 -OpBranch %152 -%152 = OpLabel -OpSelectionMerge %165 None -OpSwitch %121 %168 1 %166 2 %167 -%166 = OpLabel -OpBranch %165 -%167 = OpLabel -OpSelectionMerge %169 None -OpSwitch %122 %171 1 %170 -%170 = OpLabel +OpBranch %151 +%151 = OpLabel +OpLoopMerge %152 %154 None +OpBranch %156 +%156 = OpLabel +%157 = OpLoad %49 %155 +%158 = OpIEqual %52 %56 %157 +%159 = OpAll %51 %158 +OpSelectionMerge %160 None +OpBranchConditional %159 %152 %160 +%160 = OpLabel +%161 = OpCompositeExtract %4 %157 1 +%162 = OpIEqual %51 %161 %55 +%163 = OpSelect %4 %162 %54 %23 +%164 = OpCompositeConstruct %49 %163 %54 +%165 = OpIAdd %49 %157 %164 +OpStore %155 %165 OpBranch %153 -%171 = OpLabel -OpSelectionMerge %172 None -OpSwitch %123 %174 1 %173 -%173 = OpLabel -OpStore %129 %128 -OpBranch %172 +%153 = OpLabel +OpSelectionMerge %166 None +OpSwitch %140 %168 1 %167 +%167 = OpLabel +OpStore %148 %146 +OpBranch %166 +%168 = OpLabel +OpBranch %166 +%166 = OpLabel +OpBranch %154 +%154 = OpLabel +OpBranch %151 +%152 = OpLabel +OpBranch %169 +%169 = OpLabel +OpLoopMerge %170 %172 None +OpBranch %174 %174 = OpLabel +%175 = OpLoad %49 %173 +%176 = OpIEqual %52 %56 %175 +%177 = OpAll %51 %176 +OpSelectionMerge %178 None +OpBranchConditional %177 %170 %178 +%178 = OpLabel +%179 = OpCompositeExtract %4 %175 1 +%180 = OpIEqual %51 %179 %55 +%181 = OpSelect %4 %180 %54 %23 +%182 = OpCompositeConstruct %49 %181 %54 +%183 = OpIAdd %49 %175 %182 +OpStore %173 %183 +OpBranch %171 +%171 = OpLabel +OpSelectionMerge %184 None +OpSwitch %140 %187 1 %185 2 %186 +%185 = OpLabel +OpBranch %184 +%186 = OpLabel +OpSelectionMerge %188 None +OpSwitch %141 %190 1 %189 +%189 = OpLabel +OpBranch %172 +%190 = OpLabel +OpSelectionMerge %191 None +OpSwitch %142 %193 1 %192 +%192 = OpLabel +OpStore %148 %147 +OpBranch %191 +%193 = OpLabel +OpBranch %191 +%191 = OpLabel +OpBranch %188 +%188 = OpLabel +OpBranch %184 +%187 = OpLabel +OpBranch %184 +%184 = OpLabel OpBranch %172 %172 = OpLabel OpBranch %169 -%169 = OpLabel -OpBranch %165 -%168 = OpLabel -OpBranch %165 -%165 = OpLabel -OpBranch %153 -%153 = OpLabel -OpBranch %150 -%151 = OpLabel +%170 = OpLabel OpReturn OpFunctionEnd -%179 = OpFunction %2 None %15 -%175 = OpLabel -%182 = OpVariable %130 Function %183 -%178 = OpLoad %3 %176 -OpBranch %184 -%184 = OpLabel -OpControlBarrier %185 %35 %186 -OpControlBarrier %185 %185 %187 -OpControlBarrier %185 %185 %188 -OpSelectionMerge %189 None -OpSwitch %127 %190 -%190 = OpLabel -OpStore %182 %127 -OpBranch %189 -%189 = OpLabel -%191 = OpLoad %5 %182 -OpSelectionMerge %192 None -OpSwitch %191 %197 1 %193 2 %194 3 %195 4 %195 5 %196 6 %197 -%193 = OpLabel -OpStore %182 %16 -OpBranch %192 +%198 = OpFunction %2 None %15 %194 = OpLabel -OpStore %182 %127 -OpBranch %192 -%195 = OpLabel -OpStore %182 %128 -OpBranch %192 -%196 = OpLabel -OpStore %182 %180 -OpBranch %192 -%197 = OpLabel -OpStore %182 %181 -OpBranch %192 -%192 = OpLabel -OpSelectionMerge %198 None -OpSwitch %33 %200 0 %199 -%199 = OpLabel -OpBranch %198 -%200 = OpLabel -OpBranch %198 -%198 = OpLabel -%201 = OpLoad %5 %182 -OpSelectionMerge %202 None -OpSwitch %201 %207 1 %203 2 %204 3 %205 4 %206 +%201 = OpVariable %149 Function %202 +%197 = OpLoad %3 %195 +OpBranch %203 %203 = OpLabel -OpStore %182 %16 -OpBranch %202 -%204 = OpLabel -OpStore %182 %127 +OpControlBarrier %204 %54 %205 +OpControlBarrier %204 %204 %206 +OpControlBarrier %204 %204 %207 +OpSelectionMerge %208 None +OpSwitch %146 %209 +%209 = OpLabel +OpStore %201 %146 +OpBranch %208 +%208 = OpLabel +%210 = OpLoad %5 %201 +OpSelectionMerge %211 None +OpSwitch %210 %216 1 %212 2 %213 3 %214 4 %214 5 %215 6 %216 +%212 = OpLabel +OpStore %201 %16 +OpBranch %211 +%213 = OpLabel +OpStore %201 %146 +OpBranch %211 +%214 = OpLabel +OpStore %201 %147 +OpBranch %211 +%215 = OpLabel +OpStore %201 %199 +OpBranch %211 +%216 = OpLabel +OpStore %201 %200 +OpBranch %211 +%211 = OpLabel +OpSelectionMerge %217 None +OpSwitch %23 %219 0 %218 +%218 = OpLabel +OpBranch %217 +%219 = OpLabel +OpBranch %217 +%217 = OpLabel +%220 = OpLoad %5 %201 +OpSelectionMerge %221 None +OpSwitch %220 %226 1 %222 2 %223 3 %224 4 %225 +%222 = OpLabel +OpStore %201 %16 +OpBranch %221 +%223 = OpLabel +OpStore %201 %146 OpReturn -%205 = OpLabel -OpStore %182 %128 +%224 = OpLabel +OpStore %201 %147 OpReturn -%206 = OpLabel +%225 = OpLabel OpReturn -%207 = OpLabel -OpStore %182 %180 +%226 = OpLabel +OpStore %201 %199 OpReturn -%202 = OpLabel +%221 = OpLabel OpReturn OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/wgsl/control-flow.wgsl b/naga/tests/out/wgsl/control-flow.wgsl index eaee5aa214..48a5ae5825 100644 --- a/naga/tests/out/wgsl/control-flow.wgsl +++ b/naga/tests/out/wgsl/control-flow.wgsl @@ -17,6 +17,46 @@ fn switch_case_break() { return; } +fn switch_selector_type_conversion() { + switch 0u { + case 0u: { + } + default: { + } + } + switch 0u { + case 0u: { + return; + } + default: { + return; + } + } +} + +fn switch_const_expr_case_selectors() { + switch 0i { + case 0: { + return; + } + case 1: { + return; + } + case 2: { + return; + } + case 3: { + return; + } + case 4: { + return; + } + default: { + return; + } + } +} + fn loop_switch_continue(x: i32) { loop { switch x { diff --git a/naga/tests/wgsl_errors.rs b/naga/tests/wgsl_errors.rs index 7d459dd7ad..9b2392857b 100644 --- a/naga/tests/wgsl_errors.rs +++ b/naga/tests/wgsl_errors.rs @@ -1905,18 +1905,16 @@ fn switch_signed_unsigned_mismatch() { check( " fn x(y: u32) { - switch y { - case 1: {} - } + switch y { + case 1i: {} + } } ", - r###"error: invalid switch value - ┌─ wgsl:4:16 + r###"error: invalid `switch` case selector value + ┌─ wgsl:4:22 │ -4 │ case 1: {} - │ ^ expected unsigned integer - │ - = note: suffix the integer with a `u`: `1u` +4 │ case 1i: {} + │ ^^ `switch` case selector must have the same type as the `switch` selector expression "###, ); @@ -1924,18 +1922,90 @@ fn switch_signed_unsigned_mismatch() { check( " fn x(y: i32) { - switch y { - case 1u: {} - } + switch y { + case 1u: {} + } } ", - r###"error: invalid switch value - ┌─ wgsl:4:16 + r###"error: invalid `switch` case selector value + ┌─ wgsl:4:22 │ 4 │ case 1u: {} - │ ^^ expected signed integer + │ ^^ `switch` case selector must have the same type as the `switch` selector expression + +"###, + ); +} + +#[test] +fn switch_invalid_type() { + check( + " + fn x(y: f32) { + switch y { + case 1: {} + } + } + ", + r###"error: invalid `switch` selector + ┌─ wgsl:3:20 │ - = note: remove the `u` suffix: `1` +3 │ switch y { + │ ^ `switch` selector must be a scalar integer + +"###, + ); + + check( + " + fn x(y: vec2) { + switch y { + case 1: {} + } + } + ", + r###"error: invalid `switch` selector + ┌─ wgsl:3:20 + │ +3 │ switch y { + │ ^ `switch` selector must be a scalar integer + +"###, + ); + + check( + " + fn x() { + switch 0 { + case 1.0: {} + } + } + ", + r###"error: invalid `switch` case selector value + ┌─ wgsl:4:22 + │ +4 │ case 1.0: {} + │ ^^^ `switch` case selector must be a scalar integer const expression + +"###, + ); +} + +#[test] +fn switch_non_const_case() { + check( + " + fn x(y: i32) { + switch 0 { + case y: {} + } + } + ", + r###"error: invalid `switch` case selector value + ┌─ wgsl:4:22 + │ +4 │ case y: {} + │ ^ `switch` case selector must be a scalar integer const expression "###, );