writer[spirv,hlsl]: Call UnwrapRef() for splats

The TypeConstructorExpression logic that tested for splats was not considering references. This led to broken emission for the SPIR-V and HLSL backends.

Fixed: tint:992
Change-Id: I9824b71f526997f91d380c09b459f4fd73065b19
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58397
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 0a04858..fc9e505 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -1989,7 +1989,7 @@
   // vector dimension using .x
   const bool is_single_value_vector_init =
       type->is_scalar_vector() && expr->values().size() == 1 &&
-      TypeOf(expr->values()[0])->is_scalar();
+      TypeOf(expr->values()[0])->UnwrapRef()->is_scalar();
 
   auto it = structure_builders_.find(As<sem::Struct>(type));
   if (it != structure_builders_.end()) {
diff --git a/src/writer/hlsl/generator_impl_constructor_test.cc b/src/writer/hlsl/generator_impl_constructor_test.cc
index dae8ad1..9c253bc 100644
--- a/src/writer/hlsl/generator_impl_constructor_test.cc
+++ b/src/writer/hlsl/generator_impl_constructor_test.cc
@@ -116,7 +116,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor,
-       EmitConstructor_Type_Vec_SingleScalar_Float) {
+       EmitConstructor_Type_Vec_SingleScalar_Float_Literal) {
   WrapInFunction(vec3<f32>(2.0f));
 
   GeneratorImpl& gen = Build();
@@ -126,7 +126,20 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor,
-       EmitConstructor_Type_Vec_SingleScalar_Bool) {
+       EmitConstructor_Type_Vec_SingleScalar_Float_Var) {
+  auto* var = Var("v", nullptr, Expr(2.0f));
+  auto* cast = vec3<f32>(var);
+  WrapInFunction(var, cast);
+
+  GeneratorImpl& gen = Build();
+
+  ASSERT_TRUE(gen.Generate()) << gen.error();
+  EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
+  const float3 tint_symbol = float3((v).xxx);)"));
+}
+
+TEST_F(HlslGeneratorImplTest_Constructor,
+       EmitConstructor_Type_Vec_SingleScalar_Bool_Literal) {
   WrapInFunction(vec3<bool>(true));
 
   GeneratorImpl& gen = Build();
@@ -136,6 +149,19 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor,
+       EmitConstructor_Type_Vec_SingleScalar_Bool_Var) {
+  auto* var = Var("v", nullptr, Expr(true));
+  auto* cast = vec3<bool>(var);
+  WrapInFunction(var, cast);
+
+  GeneratorImpl& gen = Build();
+
+  ASSERT_TRUE(gen.Generate()) << gen.error();
+  EXPECT_THAT(gen.result(), HasSubstr(R"(bool v = true;
+  const bool3 tint_symbol = bool3((v).xxx);)"));
+}
+
+TEST_F(HlslGeneratorImplTest_Constructor,
        EmitConstructor_Type_Vec_SingleScalar_Int) {
   WrapInFunction(vec3<i32>(2));
 
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index b647fa31..8038ddd 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1453,7 +1453,7 @@
   // For a single-value vector initializer, splat the initializer value.
   auto* const init_result_type = TypeOf(init)->UnwrapRef();
   if (values.size() == 1 && init_result_type->is_scalar_vector() &&
-      TypeOf(values[0])->is_scalar()) {
+      TypeOf(values[0])->UnwrapRef()->is_scalar()) {
     size_t vec_size = init_result_type->As<sem::Vector>()->size();
     for (size_t i = 0; i < (vec_size - 1); ++i) {
       ops.push_back(ops[0]);
diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc
index f1ba7c1..0f65afe 100644
--- a/src/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/writer/spirv/builder_constructor_expression_test.cc
@@ -241,7 +241,7 @@
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Bool) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Bool_Literal) {
   auto* cast = vec2<bool>(true);
   WrapInFunction(cast);
 
@@ -255,9 +255,34 @@
 %3 = OpConstantTrue %2
 %4 = OpConstantComposite %1 %3 %3
 )");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Bool_Var) {
+  auto* var = Var("v", nullptr, Expr(true));
+  auto* cast = vec2<bool>(var);
+  WrapInFunction(var, cast);
+
+  spirv::Builder& b = Build();
+
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+  ASSERT_EQ(b.GenerateExpression(cast), 8u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
+%2 = OpConstantTrue %1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%6 = OpTypeVector %1 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(OpStore %3 %2
+%7 = OpLoad %1 %3
+%8 = OpCompositeConstruct %6 %7 %7
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32_Literal) {
   auto* cast = vec2<f32>(2.0f);
   WrapInFunction(cast);
 
@@ -271,6 +296,30 @@
 %3 = OpConstant %2 2
 %4 = OpConstantComposite %1 %3 %3
 )");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32_Var) {
+  auto* var = Var("v", nullptr, Expr(2.0f));
+  auto* cast = vec2<f32>(var);
+  WrapInFunction(var, cast);
+
+  spirv::Builder& b = Build();
+
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+  ASSERT_EQ(b.GenerateExpression(cast), 8u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 2
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%6 = OpTypeVector %1 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %3 %2
+%7 = OpLoad %1 %3
+%8 = OpCompositeConstruct %6 %7 %7
+)");
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32_F32) {
diff --git a/test/bug/tint/992.wgsl b/test/bug/tint/992.wgsl
new file mode 100644
index 0000000..e60c597
--- /dev/null
+++ b/test/bug/tint/992.wgsl
@@ -0,0 +1,6 @@
+[[stage(fragment)]]
+fn frag_main() -> [[location(0)]] vec4<f32> {
+    var b: f32 = 0.0;
+    var v: vec3<f32> = vec3<f32>(b);
+    return vec4<f32>(v, 1.0);
+}
diff --git a/test/bug/tint/992.wgsl.expected.hlsl b/test/bug/tint/992.wgsl.expected.hlsl
new file mode 100644
index 0000000..f532fdf
--- /dev/null
+++ b/test/bug/tint/992.wgsl.expected.hlsl
@@ -0,0 +1,10 @@
+struct tint_symbol {
+  float4 value : SV_Target0;
+};
+
+tint_symbol frag_main() {
+  float b = 0.0f;
+  float3 v = float3((b).xxx);
+  const tint_symbol tint_symbol_1 = {float4(v, 1.0f)};
+  return tint_symbol_1;
+}
diff --git a/test/bug/tint/992.wgsl.expected.msl b/test/bug/tint/992.wgsl.expected.msl
new file mode 100644
index 0000000..5438783
--- /dev/null
+++ b/test/bug/tint/992.wgsl.expected.msl
@@ -0,0 +1,14 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_symbol {
+  float4 value [[color(0)]];
+};
+
+fragment tint_symbol frag_main() {
+  float b = 0.0f;
+  float3 v = float3(b);
+  tint_symbol const tint_symbol_1 = {.value=float4(v, 1.0f)};
+  return tint_symbol_1;
+}
+
diff --git a/test/bug/tint/992.wgsl.expected.spvasm b/test/bug/tint/992.wgsl.expected.spvasm
new file mode 100644
index 0000000..ea7331b
--- /dev/null
+++ b/test/bug/tint/992.wgsl.expected.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %frag_main "frag_main" %tint_symbol_1
+               OpExecutionMode %frag_main OriginUpperLeft
+               OpName %tint_symbol_1 "tint_symbol_1"
+               OpName %tint_symbol_2 "tint_symbol_2"
+               OpName %tint_symbol "tint_symbol"
+               OpName %frag_main "frag_main"
+               OpName %b "b"
+               OpName %v "v"
+               OpDecorate %tint_symbol_1 Location 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %5 = OpConstantNull %v4float
+%tint_symbol_1 = OpVariable %_ptr_Output_v4float Output %5
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void %v4float
+         %11 = OpTypeFunction %void
+    %float_0 = OpConstant %float 0
+%_ptr_Function_float = OpTypePointer Function %float
+         %17 = OpConstantNull %float
+    %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %23 = OpConstantNull %v3float
+    %float_1 = OpConstant %float 1
+%tint_symbol_2 = OpFunction %void None %6
+%tint_symbol = OpFunctionParameter %v4float
+         %10 = OpLabel
+               OpStore %tint_symbol_1 %tint_symbol
+               OpReturn
+               OpFunctionEnd
+  %frag_main = OpFunction %void None %11
+         %13 = OpLabel
+          %b = OpVariable %_ptr_Function_float Function %17
+          %v = OpVariable %_ptr_Function_v3float Function %23
+               OpStore %b %float_0
+         %19 = OpLoad %float %b
+         %20 = OpCompositeConstruct %v3float %19 %19 %19
+               OpStore %v %20
+         %25 = OpLoad %v3float %v
+         %26 = OpCompositeExtract %float %25 0
+         %27 = OpCompositeExtract %float %25 1
+         %28 = OpCompositeExtract %float %25 2
+         %30 = OpCompositeConstruct %v4float %26 %27 %28 %float_1
+         %24 = OpFunctionCall %void %tint_symbol_2 %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/bug/tint/992.wgsl.expected.wgsl b/test/bug/tint/992.wgsl.expected.wgsl
new file mode 100644
index 0000000..7354604
--- /dev/null
+++ b/test/bug/tint/992.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+[[stage(fragment)]]
+fn frag_main() -> [[location(0)]] vec4<f32> {
+  var b : f32 = 0.0;
+  var v : vec3<f32> = vec3<f32>(b);
+  return vec4<f32>(v, 1.0);
+}
diff --git a/test/expressions/splat/var/bool.wgsl b/test/expressions/splat/var/bool.wgsl
new file mode 100644
index 0000000..d372f94
--- /dev/null
+++ b/test/expressions/splat/var/bool.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+    var v = true || false;
+    var v2 : vec2<bool> = vec2<bool>(v);
+    var v3 : vec3<bool> = vec3<bool>(v);
+    var v4 : vec4<bool> = vec4<bool>(v);
+}
diff --git a/test/expressions/splat/var/bool.wgsl.expected.hlsl b/test/expressions/splat/var/bool.wgsl.expected.hlsl
new file mode 100644
index 0000000..76b97e2
--- /dev/null
+++ b/test/expressions/splat/var/bool.wgsl.expected.hlsl
@@ -0,0 +1,15 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  bool tint_tmp = true;
+  if (!tint_tmp) {
+    tint_tmp = false;
+  }
+  bool v = (tint_tmp);
+  bool2 v2 = bool2((v).xx);
+  bool3 v3 = bool3((v).xxx);
+  bool4 v4 = bool4((v).xxxx);
+}
diff --git a/test/expressions/splat/var/bool.wgsl.expected.msl b/test/expressions/splat/var/bool.wgsl.expected.msl
new file mode 100644
index 0000000..968f6c6
--- /dev/null
+++ b/test/expressions/splat/var/bool.wgsl.expected.msl
@@ -0,0 +1,10 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  bool v = (true || false);
+  bool2 v2 = bool2(v);
+  bool3 v3 = bool3(v);
+  bool4 v4 = bool4(v);
+}
+
diff --git a/test/expressions/splat/var/bool.wgsl.expected.spvasm b/test/expressions/splat/var/bool.wgsl.expected.spvasm
new file mode 100644
index 0000000..155f6e6
--- /dev/null
+++ b/test/expressions/splat/var/bool.wgsl.expected.spvasm
@@ -0,0 +1,59 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+               OpName %v2 "v2"
+               OpName %v3 "v3"
+               OpName %v4 "v4"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+      %false = OpConstantFalse %bool
+%_ptr_Function_bool = OpTypePointer Function %bool
+         %15 = OpConstantNull %bool
+     %v2bool = OpTypeVector %bool 2
+%_ptr_Function_v2bool = OpTypePointer Function %v2bool
+         %21 = OpConstantNull %v2bool
+     %v3bool = OpTypeVector %bool 3
+%_ptr_Function_v3bool = OpTypePointer Function %v3bool
+         %27 = OpConstantNull %v3bool
+     %v4bool = OpTypeVector %bool 4
+%_ptr_Function_v4bool = OpTypePointer Function %v4bool
+         %33 = OpConstantNull %v4bool
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %1
+          %6 = OpLabel
+          %v = OpVariable %_ptr_Function_bool Function %15
+         %v2 = OpVariable %_ptr_Function_v2bool Function %21
+         %v3 = OpVariable %_ptr_Function_v3bool Function %27
+         %v4 = OpVariable %_ptr_Function_v4bool Function %33
+               OpSelectionMerge %9 None
+               OpBranchConditional %true %9 %10
+         %10 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+         %12 = OpPhi %bool %true %6 %false %10
+               OpStore %v %12
+         %17 = OpLoad %bool %v
+         %18 = OpCompositeConstruct %v2bool %17 %17
+               OpStore %v2 %18
+         %23 = OpLoad %bool %v
+         %24 = OpCompositeConstruct %v3bool %23 %23 %23
+               OpStore %v3 %24
+         %29 = OpLoad %bool %v
+         %30 = OpCompositeConstruct %v4bool %29 %29 %29 %29
+               OpStore %v4 %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/expressions/splat/var/bool.wgsl.expected.wgsl b/test/expressions/splat/var/bool.wgsl.expected.wgsl
new file mode 100644
index 0000000..698b139
--- /dev/null
+++ b/test/expressions/splat/var/bool.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+  var v = (true || false);
+  var v2 : vec2<bool> = vec2<bool>(v);
+  var v3 : vec3<bool> = vec3<bool>(v);
+  var v4 : vec4<bool> = vec4<bool>(v);
+}
diff --git a/test/expressions/splat/var/f32.wgsl b/test/expressions/splat/var/f32.wgsl
new file mode 100644
index 0000000..7645433
--- /dev/null
+++ b/test/expressions/splat/var/f32.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+    var v = 1.0 + 2.0;
+    var v2 : vec2<f32> = vec2<f32>(v);
+    var v3 : vec3<f32> = vec3<f32>(v);
+    var v4 : vec4<f32> = vec4<f32>(v);
+}
diff --git a/test/expressions/splat/var/f32.wgsl.expected.hlsl b/test/expressions/splat/var/f32.wgsl.expected.hlsl
new file mode 100644
index 0000000..00091d7
--- /dev/null
+++ b/test/expressions/splat/var/f32.wgsl.expected.hlsl
@@ -0,0 +1,11 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  float v = (1.0f + 2.0f);
+  float2 v2 = float2((v).xx);
+  float3 v3 = float3((v).xxx);
+  float4 v4 = float4((v).xxxx);
+}
diff --git a/test/expressions/splat/var/f32.wgsl.expected.msl b/test/expressions/splat/var/f32.wgsl.expected.msl
new file mode 100644
index 0000000..101d125
--- /dev/null
+++ b/test/expressions/splat/var/f32.wgsl.expected.msl
@@ -0,0 +1,10 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float v = (1.0f + 2.0f);
+  float2 v2 = float2(v);
+  float3 v3 = float3(v);
+  float4 v4 = float4(v);
+}
+
diff --git a/test/expressions/splat/var/f32.wgsl.expected.spvasm b/test/expressions/splat/var/f32.wgsl.expected.spvasm
new file mode 100644
index 0000000..5d45b4a
--- /dev/null
+++ b/test/expressions/splat/var/f32.wgsl.expected.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+               OpName %v2 "v2"
+               OpName %v3 "v3"
+               OpName %v4 "v4"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+%_ptr_Function_float = OpTypePointer Function %float
+         %13 = OpConstantNull %float
+    %v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+         %19 = OpConstantNull %v2float
+    %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %25 = OpConstantNull %v3float
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+         %31 = OpConstantNull %v4float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %1
+          %6 = OpLabel
+          %v = OpVariable %_ptr_Function_float Function %13
+         %v2 = OpVariable %_ptr_Function_v2float Function %19
+         %v3 = OpVariable %_ptr_Function_v3float Function %25
+         %v4 = OpVariable %_ptr_Function_v4float Function %31
+         %10 = OpFAdd %float %float_1 %float_2
+               OpStore %v %10
+         %15 = OpLoad %float %v
+         %16 = OpCompositeConstruct %v2float %15 %15
+               OpStore %v2 %16
+         %21 = OpLoad %float %v
+         %22 = OpCompositeConstruct %v3float %21 %21 %21
+               OpStore %v3 %22
+         %27 = OpLoad %float %v
+         %28 = OpCompositeConstruct %v4float %27 %27 %27 %27
+               OpStore %v4 %28
+               OpReturn
+               OpFunctionEnd
diff --git a/test/expressions/splat/var/f32.wgsl.expected.wgsl b/test/expressions/splat/var/f32.wgsl.expected.wgsl
new file mode 100644
index 0000000..0579720
--- /dev/null
+++ b/test/expressions/splat/var/f32.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+  var v = (1.0 + 2.0);
+  var v2 : vec2<f32> = vec2<f32>(v);
+  var v3 : vec3<f32> = vec3<f32>(v);
+  var v4 : vec4<f32> = vec4<f32>(v);
+}
diff --git a/test/expressions/splat/var/i32.wgsl b/test/expressions/splat/var/i32.wgsl
new file mode 100644
index 0000000..7601bef
--- /dev/null
+++ b/test/expressions/splat/var/i32.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+    var v = 1 + 2;
+    var v2 : vec2<i32> = vec2<i32>(v);
+    var v3 : vec3<i32> = vec3<i32>(v);
+    var v4 : vec4<i32> = vec4<i32>(v);
+}
diff --git a/test/expressions/splat/var/i32.wgsl.expected.hlsl b/test/expressions/splat/var/i32.wgsl.expected.hlsl
new file mode 100644
index 0000000..c9905a8
--- /dev/null
+++ b/test/expressions/splat/var/i32.wgsl.expected.hlsl
@@ -0,0 +1,11 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  int v = (1 + 2);
+  int2 v2 = int2((v).xx);
+  int3 v3 = int3((v).xxx);
+  int4 v4 = int4((v).xxxx);
+}
diff --git a/test/expressions/splat/var/i32.wgsl.expected.msl b/test/expressions/splat/var/i32.wgsl.expected.msl
new file mode 100644
index 0000000..30ae6c0
--- /dev/null
+++ b/test/expressions/splat/var/i32.wgsl.expected.msl
@@ -0,0 +1,10 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int v = (1 + 2);
+  int2 v2 = int2(v);
+  int3 v3 = int3(v);
+  int4 v4 = int4(v);
+}
+
diff --git a/test/expressions/splat/var/i32.wgsl.expected.spvasm b/test/expressions/splat/var/i32.wgsl.expected.spvasm
new file mode 100644
index 0000000..157b2da
--- /dev/null
+++ b/test/expressions/splat/var/i32.wgsl.expected.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+               OpName %v2 "v2"
+               OpName %v3 "v3"
+               OpName %v4 "v4"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+%_ptr_Function_int = OpTypePointer Function %int
+         %13 = OpConstantNull %int
+      %v2int = OpTypeVector %int 2
+%_ptr_Function_v2int = OpTypePointer Function %v2int
+         %19 = OpConstantNull %v2int
+      %v3int = OpTypeVector %int 3
+%_ptr_Function_v3int = OpTypePointer Function %v3int
+         %25 = OpConstantNull %v3int
+      %v4int = OpTypeVector %int 4
+%_ptr_Function_v4int = OpTypePointer Function %v4int
+         %31 = OpConstantNull %v4int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %1
+          %6 = OpLabel
+          %v = OpVariable %_ptr_Function_int Function %13
+         %v2 = OpVariable %_ptr_Function_v2int Function %19
+         %v3 = OpVariable %_ptr_Function_v3int Function %25
+         %v4 = OpVariable %_ptr_Function_v4int Function %31
+         %10 = OpIAdd %int %int_1 %int_2
+               OpStore %v %10
+         %15 = OpLoad %int %v
+         %16 = OpCompositeConstruct %v2int %15 %15
+               OpStore %v2 %16
+         %21 = OpLoad %int %v
+         %22 = OpCompositeConstruct %v3int %21 %21 %21
+               OpStore %v3 %22
+         %27 = OpLoad %int %v
+         %28 = OpCompositeConstruct %v4int %27 %27 %27 %27
+               OpStore %v4 %28
+               OpReturn
+               OpFunctionEnd
diff --git a/test/expressions/splat/var/i32.wgsl.expected.wgsl b/test/expressions/splat/var/i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..e1068a2
--- /dev/null
+++ b/test/expressions/splat/var/i32.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+  var v = (1 + 2);
+  var v2 : vec2<i32> = vec2<i32>(v);
+  var v3 : vec3<i32> = vec3<i32>(v);
+  var v4 : vec4<i32> = vec4<i32>(v);
+}
diff --git a/test/expressions/splat/var/u32.wgsl b/test/expressions/splat/var/u32.wgsl
new file mode 100644
index 0000000..2989f49
--- /dev/null
+++ b/test/expressions/splat/var/u32.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+    var v = 1u + 2u;
+    var v2 : vec2<u32> = vec2<u32>(v);
+    var v3 : vec3<u32> = vec3<u32>(v);
+    var v4 : vec4<u32> = vec4<u32>(v);
+}
diff --git a/test/expressions/splat/var/u32.wgsl.expected.hlsl b/test/expressions/splat/var/u32.wgsl.expected.hlsl
new file mode 100644
index 0000000..5e51b8d
--- /dev/null
+++ b/test/expressions/splat/var/u32.wgsl.expected.hlsl
@@ -0,0 +1,11 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  uint v = (1u + 2u);
+  uint2 v2 = uint2((v).xx);
+  uint3 v3 = uint3((v).xxx);
+  uint4 v4 = uint4((v).xxxx);
+}
diff --git a/test/expressions/splat/var/u32.wgsl.expected.msl b/test/expressions/splat/var/u32.wgsl.expected.msl
new file mode 100644
index 0000000..28df296
--- /dev/null
+++ b/test/expressions/splat/var/u32.wgsl.expected.msl
@@ -0,0 +1,10 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  uint v = (1u + 2u);
+  uint2 v2 = uint2(v);
+  uint3 v3 = uint3(v);
+  uint4 v4 = uint4(v);
+}
+
diff --git a/test/expressions/splat/var/u32.wgsl.expected.spvasm b/test/expressions/splat/var/u32.wgsl.expected.spvasm
new file mode 100644
index 0000000..ed6444a
--- /dev/null
+++ b/test/expressions/splat/var/u32.wgsl.expected.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+               OpName %v2 "v2"
+               OpName %v3 "v3"
+               OpName %v4 "v4"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+         %19 = OpConstantNull %v2uint
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Function_v3uint = OpTypePointer Function %v3uint
+         %25 = OpConstantNull %v3uint
+     %v4uint = OpTypeVector %uint 4
+%_ptr_Function_v4uint = OpTypePointer Function %v4uint
+         %31 = OpConstantNull %v4uint
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %1
+          %6 = OpLabel
+          %v = OpVariable %_ptr_Function_uint Function %13
+         %v2 = OpVariable %_ptr_Function_v2uint Function %19
+         %v3 = OpVariable %_ptr_Function_v3uint Function %25
+         %v4 = OpVariable %_ptr_Function_v4uint Function %31
+         %10 = OpIAdd %uint %uint_1 %uint_2
+               OpStore %v %10
+         %15 = OpLoad %uint %v
+         %16 = OpCompositeConstruct %v2uint %15 %15
+               OpStore %v2 %16
+         %21 = OpLoad %uint %v
+         %22 = OpCompositeConstruct %v3uint %21 %21 %21
+               OpStore %v3 %22
+         %27 = OpLoad %uint %v
+         %28 = OpCompositeConstruct %v4uint %27 %27 %27 %27
+               OpStore %v4 %28
+               OpReturn
+               OpFunctionEnd
diff --git a/test/expressions/splat/var/u32.wgsl.expected.wgsl b/test/expressions/splat/var/u32.wgsl.expected.wgsl
new file mode 100644
index 0000000..56149a5
--- /dev/null
+++ b/test/expressions/splat/var/u32.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+fn f() {
+  var v = (1u + 2u);
+  var v2 : vec2<u32> = vec2<u32>(v);
+  var v3 : vec3<u32> = vec3<u32>(v);
+  var v4 : vec4<u32> = vec4<u32>(v);
+}