Add support for converting bool to other types

The only non-trivial case is SPIR-V, which generates OpSelect to
choose between 1 or 0.

Fixed: tint:997
Change-Id: Ifda7b3ec1e0a713843a2da7ed59c3449d4eec8bd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58521
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index ca5ea27..6a4d7b3 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -2752,11 +2752,7 @@
       // A mismatch of vector type parameter T is only an error if multiple
       // arguments are present. A single argument constructor constitutes a
       // type conversion expression.
-      // NOTE: A conversion expression from a vec<bool> to any other vecN<T>
-      // is disallowed (see
-      // https://gpuweb.github.io/gpuweb/wgsl.html#conversion-expr).
-      if (elem_type != value_elem_type &&
-          (values.size() > 1u || value_vec->is_bool_vector())) {
+      if (elem_type != value_elem_type && values.size() > 1u) {
         AddError(
             "type in vector constructor does not match vector type: "
             "expected '" +
@@ -2878,11 +2874,10 @@
   using U32 = sem::U32;
   using F32 = sem::F32;
 
-  const bool is_valid =
-      (type->Is<Bool>() && value_type->IsAnyOf<Bool, I32, U32, F32>()) ||
-      (type->Is<I32>() && value_type->IsAnyOf<I32, U32, F32>()) ||
-      (type->Is<U32>() && value_type->IsAnyOf<I32, U32, F32>()) ||
-      (type->Is<F32>() && value_type->IsAnyOf<I32, U32, F32>());
+  const bool is_valid = (type->Is<Bool>() && value_type->is_scalar()) ||
+                        (type->Is<I32>() && value_type->is_scalar()) ||
+                        (type->Is<U32>() && value_type->is_scalar()) ||
+                        (type->Is<F32>() && value_type->is_scalar());
   if (!is_valid) {
     AddError("cannot construct '" + type_name + "' with a value of type '" +
                  TypeNameOf(value) + "'",
diff --git a/src/resolver/type_constructor_validation_test.cc b/src/resolver/type_constructor_validation_test.cc
index d363f8c..2f67ce0 100644
--- a/src/resolver/type_constructor_validation_test.cc
+++ b/src/resolver/type_constructor_validation_test.cc
@@ -255,27 +255,33 @@
     ParamsFor<bool, i32>(),  //
     ParamsFor<bool, f32>(),  //
 
-    ParamsFor<i32, u32>(),  //
-    ParamsFor<i32, f32>(),  //
+    ParamsFor<i32, bool>(),  //
+    ParamsFor<i32, u32>(),   //
+    ParamsFor<i32, f32>(),   //
 
-    ParamsFor<u32, i32>(),  //
-    ParamsFor<u32, f32>(),  //
+    ParamsFor<u32, bool>(),  //
+    ParamsFor<u32, i32>(),   //
+    ParamsFor<u32, f32>(),   //
 
-    ParamsFor<f32, u32>(),  //
-    ParamsFor<f32, i32>(),  //
+    ParamsFor<f32, bool>(),  //
+    ParamsFor<f32, u32>(),   //
+    ParamsFor<f32, i32>(),   //
 
     ParamsFor<vec3<bool>, vec3<u32>>(),  //
     ParamsFor<vec3<bool>, vec3<i32>>(),  //
     ParamsFor<vec3<bool>, vec3<f32>>(),  //
 
-    ParamsFor<vec3<i32>, vec3<u32>>(),  //
-    ParamsFor<vec3<i32>, vec3<f32>>(),  //
+    ParamsFor<vec3<i32>, vec3<bool>>(),  //
+    ParamsFor<vec3<i32>, vec3<u32>>(),   //
+    ParamsFor<vec3<i32>, vec3<f32>>(),   //
 
-    ParamsFor<vec3<u32>, vec3<i32>>(),  //
-    ParamsFor<vec3<u32>, vec3<f32>>(),  //
+    ParamsFor<vec3<u32>, vec3<bool>>(),  //
+    ParamsFor<vec3<u32>, vec3<i32>>(),   //
+    ParamsFor<vec3<u32>, vec3<f32>>(),   //
 
-    ParamsFor<vec3<f32>, vec3<u32>>(),  //
-    ParamsFor<vec3<f32>, vec3<i32>>(),  //
+    ParamsFor<vec3<f32>, vec3<bool>>(),  //
+    ParamsFor<vec3<f32>, vec3<u32>>(),   //
+    ParamsFor<vec3<f32>, vec3<i32>>(),   //
 };
 
 using ConversionConstructorValidTest = ResolverTestWithParam<Params>;
@@ -388,13 +394,15 @@
 
 TEST_F(ResolverTypeConstructorValidationTest,
        ConversionConstructorInvalid_InvalidInitializer) {
-  auto* a = Var("a", ty.f32(), ast::StorageClass::kNone,
-                Construct(Source{{12, 34}}, ty.f32(), Expr(true)));
+  auto* a =
+      Var("a", ty.f32(), ast::StorageClass::kNone,
+          Construct(Source{{12, 34}}, ty.f32(), Construct(ty.array<f32, 4>())));
   WrapInFunction(a);
 
   ASSERT_FALSE(r()->Resolve());
   ASSERT_EQ(r()->error(),
-            "12:34 error: cannot construct 'f32' with a value of type 'bool'");
+            "12:34 error: cannot construct 'f32' with a value of type "
+            "'array<f32, 4>'");
 }
 
 }  // namespace ConversionConstructorTest
@@ -717,20 +725,6 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
-       Expr_Constructor_Vec2_Error_InvalidConversionFromVec2Bool) {
-  SetSource(Source::Location({12, 34}));
-
-  auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>(
-      Source{{12, 34}}, ty.vec2<bool>(), ExprList()));
-  WrapInFunction(tc);
-
-  EXPECT_FALSE(r()->Resolve());
-  EXPECT_EQ(r()->error(),
-            "12:34 error: type in vector constructor does not match vector "
-            "type: expected 'f32', found 'bool'");
-}
-
-TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2_Error_InvalidArgumentType) {
   auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>(
       Source{{12, 34}}, ty.mat2x2<f32>(), ExprList()));
@@ -987,18 +981,6 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
-       Expr_Constructor_Vec3_Error_InvalidConversionFromVec3Bool) {
-  auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>(
-      Source{{12, 34}}, ty.vec3<bool>(), ExprList()));
-  WrapInFunction(tc);
-
-  EXPECT_FALSE(r()->Resolve());
-  EXPECT_EQ(r()->error(),
-            "12:34 error: type in vector constructor does not match vector "
-            "type: expected 'f32', found 'bool'");
-}
-
-TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3_Error_InvalidArgumentType) {
   auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>(
       Source{{12, 34}}, ty.mat2x2<f32>(), ExprList()));
@@ -1349,18 +1331,6 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
-       Expr_Constructor_Vec4_Error_InvalidConversionFromVec4Bool) {
-  auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>(
-      Source{{12, 34}}, ty.vec4<bool>(), ExprList()));
-  WrapInFunction(tc);
-
-  EXPECT_FALSE(r()->Resolve());
-  EXPECT_EQ(r()->error(),
-            "12:34 error: type in vector constructor does not match vector "
-            "type: expected 'f32', found 'bool'");
-}
-
-TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_InvalidArgumentType) {
   auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>(
       Source{{12, 34}}, ty.mat2x2<f32>(), ExprList()));
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 8038ddd..039269f 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1568,7 +1568,51 @@
     }
 
     return result_id;
+  } else if (from_type->is_bool_scalar_or_vector() &&
+             to_type->is_numeric_scalar_or_vector()) {
+    // Convert bool scalar/vector to numeric scalar/vector.
+    // Use the bool to select between 1 (if true) and 0 (if false).
 
+    const auto* to_elem_type = elem_type_of(to_type);
+    uint32_t one_id;
+    uint32_t zero_id;
+    if (to_elem_type->Is<sem::F32>()) {
+      ast::FloatLiteral one(ProgramID(), Source{}, 1.0f);
+      ast::FloatLiteral zero(ProgramID(), Source{}, 0.0f);
+      one_id = GenerateLiteralIfNeeded(nullptr, &one);
+      zero_id = GenerateLiteralIfNeeded(nullptr, &zero);
+    } else if (to_elem_type->Is<sem::U32>()) {
+      ast::UintLiteral one(ProgramID(), Source{}, 1);
+      ast::UintLiteral zero(ProgramID(), Source{}, 0);
+      one_id = GenerateLiteralIfNeeded(nullptr, &one);
+      zero_id = GenerateLiteralIfNeeded(nullptr, &zero);
+    } else if (to_elem_type->Is<sem::I32>()) {
+      ast::SintLiteral one(ProgramID(), Source{}, 1);
+      ast::SintLiteral zero(ProgramID(), Source{}, 0);
+      one_id = GenerateLiteralIfNeeded(nullptr, &one);
+      zero_id = GenerateLiteralIfNeeded(nullptr, &zero);
+    } else {
+      error_ = "invalid destination type for bool conversion";
+      return false;
+    }
+    if (auto* to_vec = to_type->As<sem::Vector>()) {
+      // Splat the scalars into vectors.
+      one_id = GenerateConstantVectorSplatIfNeeded(to_vec, one_id);
+      zero_id = GenerateConstantVectorSplatIfNeeded(to_vec, zero_id);
+    }
+    if (!one_id || !zero_id) {
+      return false;
+    }
+
+    op = spv::Op::OpSelect;
+    if (!push_function_inst(
+            op, {Operand::Int(result_type_id), Operand::Int(result_id),
+                 Operand::Int(val_id), Operand::Int(one_id),
+                 Operand::Int(zero_id)})) {
+      return 0;
+    }
+
+    return result_id;
   } else {
     TINT_ICE(Writer, builder_.Diagnostics()) << "Invalid from_type";
   }
@@ -1720,6 +1764,31 @@
   return result_id;
 }
 
+uint32_t Builder::GenerateConstantVectorSplatIfNeeded(const sem::Vector* type,
+                                                      uint32_t value_id) {
+  auto type_id = GenerateTypeIfNeeded(type);
+  if (type_id == 0 || value_id == 0) {
+    return 0;
+  }
+
+  uint64_t key = (static_cast<uint64_t>(type->size()) << 32) + value_id;
+  return utils::GetOrCreate(const_splat_to_id_, key, [&] {
+    auto result = result_op();
+    auto result_id = result.to_i();
+
+    OperandList ops;
+    ops.push_back(Operand::Int(type_id));
+    ops.push_back(result);
+    for (uint32_t i = 0; i < type->size(); i++) {
+      ops.push_back(Operand::Int(value_id));
+    }
+    push_type(spv::Op::OpConstantComposite, ops);
+
+    const_splat_to_id_[key] = result_id;
+    return result_id;
+  });
+}
+
 uint32_t Builder::GenerateShortCircuitBinaryExpression(
     ast::BinaryExpression* expr) {
   auto lhs_id = GenerateExpression(expr->lhs());
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 5ad065f..a81da1e 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -536,7 +536,7 @@
     return builder_.TypeOf(expr);
   }
 
-  /// Generates a constant if needed
+  /// Generates a scalar constant if needed
   /// @param constant the constant to generate.
   /// @returns the ID on success or 0 on failure
   uint32_t GenerateConstantIfNeeded(const ScalarConstant& constant);
@@ -546,6 +546,13 @@
   /// @returns the ID on success or 0 on failure
   uint32_t GenerateConstantNullIfNeeded(const sem::Type* type);
 
+  /// Generates a vector constant splat if needed
+  /// @param type the type of the vector to generate
+  /// @param value_id the ID of the scalar value to splat
+  /// @returns the ID on success or 0 on failure
+  uint32_t GenerateConstantVectorSplatIfNeeded(const sem::Vector* type,
+                                               uint32_t value_id);
+
   ProgramBuilder builder_;
   std::string error_;
   uint32_t next_id_ = 1;
@@ -567,6 +574,7 @@
   std::unordered_map<ScalarConstant, uint32_t> const_to_id_;
   std::unordered_map<std::string, uint32_t> type_constructor_to_id_;
   std::unordered_map<std::string, uint32_t> const_null_to_id_;
+  std::unordered_map<uint64_t, uint32_t> const_splat_to_id_;
   std::unordered_map<std::string, uint32_t>
       texture_type_name_to_sampled_image_type_id_;
   ScopeStack<uint32_t> scope_stack_;
diff --git a/test/types/function_scope_var_conversions.wgsl b/test/types/function_scope_var_conversions.wgsl
index 7f18422..66cc378 100644
--- a/test/types/function_scope_var_conversions.wgsl
+++ b/test/types/function_scope_var_conversions.wgsl
@@ -11,9 +11,11 @@
 
   var i32_var1 : i32 = i32(123u);
   var i32_var2 : i32 = i32(123.0);
+  var i32_var3 : i32 = i32(true);
 
   var u32_var1 : u32 = u32(123);
   var u32_var2 : u32 = u32(123.0);
+  var u32_var3 : u32 = u32(true);
 
   var v3bool_var1 : vec3<bool> = vec3<bool>(vec3<u32>(123u));
   var v3bool_var11 : vec3<bool> = vec3<bool>(vec3<u32>(1234u));
@@ -22,9 +24,11 @@
 
   var v3i32_var1 : vec3<i32> = vec3<i32>(vec3<u32>(123u));
   var v3i32_var2 : vec3<i32> = vec3<i32>(vec3<f32>(123.0));
+  var v3i32_var3 : vec3<i32> = vec3<i32>(vec3<bool>(true));
 
   var v3u32_var1 : vec3<u32> = vec3<u32>(vec3<i32>(123));
   var v3u32_var2 : vec3<u32> = vec3<u32>(vec3<f32>(123.0));
+  var v3u32_var3 : vec3<u32> = vec3<u32>(vec3<bool>(true));
 
   var v3bool_var4 : vec3<bool> = vec3<bool>(vec2<bool>(vec2<f32>(123.0)), true);
   var v4bool_var5 : vec4<bool> = vec4<bool>(vec2<bool>(vec2<f32>(123.0, 0.0)), vec2<bool>(true, bool(f32(0.0))));
diff --git a/test/types/function_scope_var_conversions.wgsl.expected.hlsl b/test/types/function_scope_var_conversions.wgsl.expected.hlsl
index 4131d52..b0f76c7 100644
--- a/test/types/function_scope_var_conversions.wgsl.expected.hlsl
+++ b/test/types/function_scope_var_conversions.wgsl.expected.hlsl
@@ -10,16 +10,20 @@
   bool bool_var3 = bool(123.0f);
   int i32_var1 = int(123u);
   int i32_var2 = int(123.0f);
+  int i32_var3 = int(true);
   uint u32_var1 = uint(123);
   uint u32_var2 = uint(123.0f);
+  uint u32_var3 = uint(true);
   bool3 v3bool_var1 = bool3(uint3((123u).xxx));
   bool3 v3bool_var11 = bool3(uint3((1234u).xxx));
   bool3 v3bool_var2 = bool3(int3((123).xxx));
   bool3 v3bool_var3 = bool3(float3((123.0f).xxx));
   int3 v3i32_var1 = int3(uint3((123u).xxx));
   int3 v3i32_var2 = int3(float3((123.0f).xxx));
+  int3 v3i32_var3 = int3(bool3((true).xxx));
   uint3 v3u32_var1 = uint3(int3((123).xxx));
   uint3 v3u32_var2 = uint3(float3((123.0f).xxx));
+  uint3 v3u32_var3 = uint3(bool3((true).xxx));
   bool3 v3bool_var4 = bool3(bool2(float2((123.0f).xx)), true);
   bool4 v4bool_var5 = bool4(bool2(float2(123.0f, 0.0f)), bool2(true, bool(float(0.0f))));
   return;
diff --git a/test/types/function_scope_var_conversions.wgsl.expected.msl b/test/types/function_scope_var_conversions.wgsl.expected.msl
index dac4d9b..a656406 100644
--- a/test/types/function_scope_var_conversions.wgsl.expected.msl
+++ b/test/types/function_scope_var_conversions.wgsl.expected.msl
@@ -12,16 +12,20 @@
   bool bool_var3 = bool(123.0f);
   int i32_var1 = int(123u);
   int i32_var2 = int(123.0f);
+  int i32_var3 = int(true);
   uint u32_var1 = uint(123);
   uint u32_var2 = uint(123.0f);
+  uint u32_var3 = uint(true);
   bool3 v3bool_var1 = bool3(uint3(123u));
   bool3 v3bool_var11 = bool3(uint3(1234u));
   bool3 v3bool_var2 = bool3(int3(123));
   bool3 v3bool_var3 = bool3(float3(123.0f));
   int3 v3i32_var1 = int3(uint3(123u));
   int3 v3i32_var2 = int3(float3(123.0f));
+  int3 v3i32_var3 = int3(bool3(true));
   uint3 v3u32_var1 = uint3(int3(123));
   uint3 v3u32_var2 = uint3(float3(123.0f));
+  uint3 v3u32_var3 = uint3(bool3(true));
   bool3 v3bool_var4 = bool3(bool2(float2(123.0f)), true);
   bool4 v4bool_var5 = bool4(bool2(float2(123.0f, 0.0f)), bool2(true, bool(float(0.0f))));
   return;
diff --git a/test/types/function_scope_var_conversions.wgsl.expected.spvasm b/test/types/function_scope_var_conversions.wgsl.expected.spvasm
index aaab7e0..1bf5ab9 100644
--- a/test/types/function_scope_var_conversions.wgsl.expected.spvasm
+++ b/test/types/function_scope_var_conversions.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 65
+; Bound: 73
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -16,16 +16,20 @@
                OpName %bool_var3 "bool_var3"
                OpName %i32_var1 "i32_var1"
                OpName %i32_var2 "i32_var2"
+               OpName %i32_var3 "i32_var3"
                OpName %u32_var1 "u32_var1"
                OpName %u32_var2 "u32_var2"
+               OpName %u32_var3 "u32_var3"
                OpName %v3bool_var1 "v3bool_var1"
                OpName %v3bool_var11 "v3bool_var11"
                OpName %v3bool_var2 "v3bool_var2"
                OpName %v3bool_var3 "v3bool_var3"
                OpName %v3i32_var1 "v3i32_var1"
                OpName %v3i32_var2 "v3i32_var2"
+               OpName %v3i32_var3 "v3i32_var3"
                OpName %v3u32_var1 "v3u32_var1"
                OpName %v3u32_var2 "v3u32_var2"
+               OpName %v3u32_var3 "v3u32_var3"
                OpName %v3bool_var4 "v3bool_var4"
                OpName %v4bool_var5 "v4bool_var5"
        %void = OpTypeVoid
@@ -45,28 +49,32 @@
     %int_123 = OpConstant %int 123
 %_ptr_Function_int = OpTypePointer Function %int
          %29 = OpConstantNull %int
+      %int_1 = OpConstant %int 1
        %uint = OpTypeInt 32 0
    %uint_123 = OpConstant %uint 123
 %_ptr_Function_uint = OpTypePointer Function %uint
-         %35 = OpConstantNull %uint
+         %37 = OpConstantNull %uint
+     %uint_1 = OpConstant %uint 1
      %v3bool = OpTypeVector %bool 3
-         %38 = OpConstantComposite %v3bool %true %true %true
+         %42 = OpConstantComposite %v3bool %true %true %true
 %_ptr_Function_v3bool = OpTypePointer Function %v3bool
-         %41 = OpConstantNull %v3bool
+         %45 = OpConstantNull %v3bool
       %v3int = OpTypeVector %int 3
-         %46 = OpConstantComposite %v3int %int_123 %int_123 %int_123
+         %50 = OpConstantComposite %v3int %int_123 %int_123 %int_123
 %_ptr_Function_v3int = OpTypePointer Function %v3int
-         %49 = OpConstantNull %v3int
+         %53 = OpConstantNull %v3int
+         %55 = OpConstantComposite %v3int %int_1 %int_1 %int_1
      %v3uint = OpTypeVector %uint 3
-         %52 = OpConstantComposite %v3uint %uint_123 %uint_123 %uint_123
+         %58 = OpConstantComposite %v3uint %uint_123 %uint_123 %uint_123
 %_ptr_Function_v3uint = OpTypePointer Function %v3uint
-         %55 = OpConstantNull %v3uint
-         %57 = OpConstantComposite %v3bool %true %true %true
+         %61 = OpConstantNull %v3uint
+         %63 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+         %65 = OpConstantComposite %v3bool %true %true %true
      %v4bool = OpTypeVector %bool 4
       %false = OpConstantFalse %bool
-         %61 = OpConstantComposite %v4bool %true %false %true %false
+         %69 = OpConstantComposite %v4bool %true %false %true %false
 %_ptr_Function_v4bool = OpTypePointer Function %v4bool
-         %64 = OpConstantNull %v4bool
+         %72 = OpConstantNull %v4bool
 %constant_with_non_constant = OpFunction %void None %1
           %4 = OpLabel
           %a = OpVariable %_ptr_Function_float Function %6
@@ -84,34 +92,42 @@
   %bool_var3 = OpVariable %_ptr_Function_bool Function %22
    %i32_var1 = OpVariable %_ptr_Function_int Function %29
    %i32_var2 = OpVariable %_ptr_Function_int Function %29
-   %u32_var1 = OpVariable %_ptr_Function_uint Function %35
-   %u32_var2 = OpVariable %_ptr_Function_uint Function %35
-%v3bool_var1 = OpVariable %_ptr_Function_v3bool Function %41
-%v3bool_var11 = OpVariable %_ptr_Function_v3bool Function %41
-%v3bool_var2 = OpVariable %_ptr_Function_v3bool Function %41
-%v3bool_var3 = OpVariable %_ptr_Function_v3bool Function %41
- %v3i32_var1 = OpVariable %_ptr_Function_v3int Function %49
- %v3i32_var2 = OpVariable %_ptr_Function_v3int Function %49
- %v3u32_var1 = OpVariable %_ptr_Function_v3uint Function %55
- %v3u32_var2 = OpVariable %_ptr_Function_v3uint Function %55
-%v3bool_var4 = OpVariable %_ptr_Function_v3bool Function %41
-%v4bool_var5 = OpVariable %_ptr_Function_v4bool Function %64
+   %i32_var3 = OpVariable %_ptr_Function_int Function %29
+   %u32_var1 = OpVariable %_ptr_Function_uint Function %37
+   %u32_var2 = OpVariable %_ptr_Function_uint Function %37
+   %u32_var3 = OpVariable %_ptr_Function_uint Function %37
+%v3bool_var1 = OpVariable %_ptr_Function_v3bool Function %45
+%v3bool_var11 = OpVariable %_ptr_Function_v3bool Function %45
+%v3bool_var2 = OpVariable %_ptr_Function_v3bool Function %45
+%v3bool_var3 = OpVariable %_ptr_Function_v3bool Function %45
+ %v3i32_var1 = OpVariable %_ptr_Function_v3int Function %53
+ %v3i32_var2 = OpVariable %_ptr_Function_v3int Function %53
+ %v3i32_var3 = OpVariable %_ptr_Function_v3int Function %53
+ %v3u32_var1 = OpVariable %_ptr_Function_v3uint Function %61
+ %v3u32_var2 = OpVariable %_ptr_Function_v3uint Function %61
+ %v3u32_var3 = OpVariable %_ptr_Function_v3uint Function %61
+%v3bool_var4 = OpVariable %_ptr_Function_v3bool Function %45
+%v4bool_var5 = OpVariable %_ptr_Function_v4bool Function %72
                OpStore %bool_var1 %true
                OpStore %bool_var2 %true
                OpStore %bool_var3 %true
                OpStore %i32_var1 %int_123
                OpStore %i32_var2 %int_123
+               OpStore %i32_var3 %int_1
                OpStore %u32_var1 %uint_123
                OpStore %u32_var2 %uint_123
-               OpStore %v3bool_var1 %38
-               OpStore %v3bool_var11 %38
-               OpStore %v3bool_var2 %38
-               OpStore %v3bool_var3 %38
-               OpStore %v3i32_var1 %46
-               OpStore %v3i32_var2 %46
-               OpStore %v3u32_var1 %52
-               OpStore %v3u32_var2 %52
-               OpStore %v3bool_var4 %57
-               OpStore %v4bool_var5 %61
+               OpStore %u32_var3 %uint_1
+               OpStore %v3bool_var1 %42
+               OpStore %v3bool_var11 %42
+               OpStore %v3bool_var2 %42
+               OpStore %v3bool_var3 %42
+               OpStore %v3i32_var1 %50
+               OpStore %v3i32_var2 %50
+               OpStore %v3i32_var3 %55
+               OpStore %v3u32_var1 %58
+               OpStore %v3u32_var2 %58
+               OpStore %v3u32_var3 %63
+               OpStore %v3bool_var4 %65
+               OpStore %v4bool_var5 %69
                OpReturn
                OpFunctionEnd
diff --git a/test/types/function_scope_var_conversions.wgsl.expected.wgsl b/test/types/function_scope_var_conversions.wgsl.expected.wgsl
index 6c40126..de961c6 100644
--- a/test/types/function_scope_var_conversions.wgsl.expected.wgsl
+++ b/test/types/function_scope_var_conversions.wgsl.expected.wgsl
@@ -10,16 +10,20 @@
   var bool_var3 : bool = bool(123.0);
   var i32_var1 : i32 = i32(123u);
   var i32_var2 : i32 = i32(123.0);
+  var i32_var3 : i32 = i32(true);
   var u32_var1 : u32 = u32(123);
   var u32_var2 : u32 = u32(123.0);
+  var u32_var3 : u32 = u32(true);
   var v3bool_var1 : vec3<bool> = vec3<bool>(vec3<u32>(123u));
   var v3bool_var11 : vec3<bool> = vec3<bool>(vec3<u32>(1234u));
   var v3bool_var2 : vec3<bool> = vec3<bool>(vec3<i32>(123));
   var v3bool_var3 : vec3<bool> = vec3<bool>(vec3<f32>(123.0));
   var v3i32_var1 : vec3<i32> = vec3<i32>(vec3<u32>(123u));
   var v3i32_var2 : vec3<i32> = vec3<i32>(vec3<f32>(123.0));
+  var v3i32_var3 : vec3<i32> = vec3<i32>(vec3<bool>(true));
   var v3u32_var1 : vec3<u32> = vec3<u32>(vec3<i32>(123));
   var v3u32_var2 : vec3<u32> = vec3<u32>(vec3<f32>(123.0));
+  var v3u32_var3 : vec3<u32> = vec3<u32>(vec3<bool>(true));
   var v3bool_var4 : vec3<bool> = vec3<bool>(vec2<bool>(vec2<f32>(123.0)), true);
   var v4bool_var5 : vec4<bool> = vec4<bool>(vec2<bool>(vec2<f32>(123.0, 0.0)), vec2<bool>(true, bool(f32(0.0))));
 }
diff --git a/test/types/module_scope_var_conversions.wgsl b/test/types/module_scope_var_conversions.wgsl
index e853046..935a1e4 100644
--- a/test/types/module_scope_var_conversions.wgsl
+++ b/test/types/module_scope_var_conversions.wgsl
@@ -4,9 +4,11 @@
 
 var<private> i32_var1 : i32 = i32(1u);
 var<private> i32_var2 : i32 = i32(1.0);
+var<private> i32_var3 : i32 = i32(true);
 
 var<private> u32_var1 : u32 = u32(1);
 var<private> u32_var2 : u32 = u32(1.0);
+var<private> u32_var3 : u32 = u32(true);
 
 var<private> v3bool_var1 : vec3<bool> = vec3<bool>(vec3<u32>(1u));
 var<private> v3bool_var2 : vec3<bool> = vec3<bool>(vec3<i32>(1));
@@ -14,9 +16,11 @@
 
 var<private> v3i32_var1 : vec3<i32> = vec3<i32>(vec3<u32>(1u));
 var<private> v3i32_var2 : vec3<i32> = vec3<i32>(vec3<f32>(1.0));
+var<private> v3i32_var3 : vec3<i32> = vec3<i32>(vec3<bool>(true));
 
 var<private> v3u32_var1 : vec3<u32> = vec3<u32>(vec3<i32>(1));
 var<private> v3u32_var2 : vec3<u32> = vec3<u32>(vec3<f32>(1.0));
+var<private> v3u32_var3 : vec3<u32> = vec3<u32>(vec3<bool>(true));
 
 var<private> v3bool_var4 : vec3<bool> = vec3<bool>(vec2<bool>(vec2<f32>(123.0)), true);
 var<private> v4bool_var5 : vec4<bool> = vec4<bool>(vec2<bool>(vec2<f32>(123.0, 0.0)), vec2<bool>(true, bool(f32(0.0))));
@@ -29,8 +33,10 @@
   bool_var3 = bool();
   i32_var1 = i32();
   i32_var2 = i32();
+  i32_var3 = i32();
   u32_var1 = u32();
   u32_var2 = u32();
+  u32_var3 = u32();
   v3bool_var1 = vec3<bool>();
   v3bool_var2 = vec3<bool>();
   v3bool_var3 = vec3<bool>();
@@ -38,6 +44,8 @@
   v4bool_var5 = vec4<bool>();
   v3i32_var1 = vec3<i32>();
   v3i32_var2 = vec3<i32>();
+  v3i32_var3 = vec3<i32>();
   v3u32_var1 = vec3<u32>();
   v3u32_var2 = vec3<u32>();
+  v3u32_var3 = vec3<u32>();
 }
diff --git a/test/types/module_scope_var_conversions.wgsl.expected.hlsl b/test/types/module_scope_var_conversions.wgsl.expected.hlsl
index e2ae7ad..e637207 100644
--- a/test/types/module_scope_var_conversions.wgsl.expected.hlsl
+++ b/test/types/module_scope_var_conversions.wgsl.expected.hlsl
@@ -3,15 +3,19 @@
 static bool bool_var3 = bool(1.0f);
 static int i32_var1 = int(1u);
 static int i32_var2 = int(1.0f);
+static int i32_var3 = int(true);
 static uint u32_var1 = uint(1);
 static uint u32_var2 = uint(1.0f);
+static uint u32_var3 = uint(true);
 static bool3 v3bool_var1 = bool3(uint3((1u).xxx));
 static bool3 v3bool_var2 = bool3(int3((1).xxx));
 static bool3 v3bool_var3 = bool3(float3((1.0f).xxx));
 static int3 v3i32_var1 = int3(uint3((1u).xxx));
 static int3 v3i32_var2 = int3(float3((1.0f).xxx));
+static int3 v3i32_var3 = int3(bool3((true).xxx));
 static uint3 v3u32_var1 = uint3(int3((1).xxx));
 static uint3 v3u32_var2 = uint3(float3((1.0f).xxx));
+static uint3 v3u32_var3 = uint3(bool3((true).xxx));
 static bool3 v3bool_var4 = bool3(bool2(float2((123.0f).xx)), true);
 static bool4 v4bool_var5 = bool4(bool2(float2(123.0f, 0.0f)), bool2(true, bool(float(0.0f))));
 
@@ -22,8 +26,10 @@
   bool_var3 = false;
   i32_var1 = 0;
   i32_var2 = 0;
+  i32_var3 = 0;
   u32_var1 = 0u;
   u32_var2 = 0u;
+  u32_var3 = 0u;
   v3bool_var1 = bool3(false, false, false);
   v3bool_var2 = bool3(false, false, false);
   v3bool_var3 = bool3(false, false, false);
@@ -31,7 +37,9 @@
   v4bool_var5 = bool4(false, false, false, false);
   v3i32_var1 = int3(0, 0, 0);
   v3i32_var2 = int3(0, 0, 0);
+  v3i32_var3 = int3(0, 0, 0);
   v3u32_var1 = uint3(0u, 0u, 0u);
   v3u32_var2 = uint3(0u, 0u, 0u);
+  v3u32_var3 = uint3(0u, 0u, 0u);
   return;
 }
diff --git a/test/types/module_scope_var_conversions.wgsl.expected.msl b/test/types/module_scope_var_conversions.wgsl.expected.msl
index 3cda311..072e9b0 100644
--- a/test/types/module_scope_var_conversions.wgsl.expected.msl
+++ b/test/types/module_scope_var_conversions.wgsl.expected.msl
@@ -7,33 +7,41 @@
   thread bool tint_symbol_3 = bool(1.0f);
   thread int tint_symbol_4 = int(1u);
   thread int tint_symbol_5 = int(1.0f);
-  thread uint tint_symbol_6 = uint(1);
-  thread uint tint_symbol_7 = uint(1.0f);
-  thread bool3 tint_symbol_8 = bool3(uint3(1u));
-  thread bool3 tint_symbol_9 = bool3(int3(1));
-  thread bool3 tint_symbol_10 = bool3(float3(1.0f));
-  thread bool3 tint_symbol_11 = bool3(bool2(float2(123.0f)), true);
-  thread bool4 tint_symbol_12 = bool4(bool2(float2(123.0f, 0.0f)), bool2(true, bool(float(0.0f))));
-  thread int3 tint_symbol_13 = int3(uint3(1u));
-  thread int3 tint_symbol_14 = int3(float3(1.0f));
-  thread uint3 tint_symbol_15 = uint3(int3(1));
-  thread uint3 tint_symbol_16 = uint3(float3(1.0f));
+  thread int tint_symbol_6 = int(true);
+  thread uint tint_symbol_7 = uint(1);
+  thread uint tint_symbol_8 = uint(1.0f);
+  thread uint tint_symbol_9 = uint(true);
+  thread bool3 tint_symbol_10 = bool3(uint3(1u));
+  thread bool3 tint_symbol_11 = bool3(int3(1));
+  thread bool3 tint_symbol_12 = bool3(float3(1.0f));
+  thread bool3 tint_symbol_13 = bool3(bool2(float2(123.0f)), true);
+  thread bool4 tint_symbol_14 = bool4(bool2(float2(123.0f, 0.0f)), bool2(true, bool(float(0.0f))));
+  thread int3 tint_symbol_15 = int3(uint3(1u));
+  thread int3 tint_symbol_16 = int3(float3(1.0f));
+  thread int3 tint_symbol_17 = int3(bool3(true));
+  thread uint3 tint_symbol_18 = uint3(int3(1));
+  thread uint3 tint_symbol_19 = uint3(float3(1.0f));
+  thread uint3 tint_symbol_20 = uint3(bool3(true));
   tint_symbol_1 = bool();
   tint_symbol_2 = bool();
   tint_symbol_3 = bool();
   tint_symbol_4 = int();
   tint_symbol_5 = int();
-  tint_symbol_6 = uint();
+  tint_symbol_6 = int();
   tint_symbol_7 = uint();
-  tint_symbol_8 = bool3();
-  tint_symbol_9 = bool3();
+  tint_symbol_8 = uint();
+  tint_symbol_9 = uint();
   tint_symbol_10 = bool3();
   tint_symbol_11 = bool3();
-  tint_symbol_12 = bool4();
-  tint_symbol_13 = int3();
-  tint_symbol_14 = int3();
-  tint_symbol_15 = uint3();
-  tint_symbol_16 = uint3();
+  tint_symbol_12 = bool3();
+  tint_symbol_13 = bool3();
+  tint_symbol_14 = bool4();
+  tint_symbol_15 = int3();
+  tint_symbol_16 = int3();
+  tint_symbol_17 = int3();
+  tint_symbol_18 = uint3();
+  tint_symbol_19 = uint3();
+  tint_symbol_20 = uint3();
   return;
 }
 
diff --git a/test/types/module_scope_var_conversions.wgsl.expected.spvasm b/test/types/module_scope_var_conversions.wgsl.expected.spvasm
index bfe6b27..7c8e142 100644
--- a/test/types/module_scope_var_conversions.wgsl.expected.spvasm
+++ b/test/types/module_scope_var_conversions.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 51
+; Bound: 55
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -12,15 +12,19 @@
                OpName %bool_var3 "bool_var3"
                OpName %i32_var1 "i32_var1"
                OpName %i32_var2 "i32_var2"
+               OpName %i32_var3 "i32_var3"
                OpName %u32_var1 "u32_var1"
                OpName %u32_var2 "u32_var2"
+               OpName %u32_var3 "u32_var3"
                OpName %v3bool_var1 "v3bool_var1"
                OpName %v3bool_var2 "v3bool_var2"
                OpName %v3bool_var3 "v3bool_var3"
                OpName %v3i32_var1 "v3i32_var1"
                OpName %v3i32_var2 "v3i32_var2"
+               OpName %v3i32_var3 "v3i32_var3"
                OpName %v3u32_var1 "v3u32_var1"
                OpName %v3u32_var2 "v3u32_var2"
+               OpName %v3u32_var3 "v3u32_var3"
                OpName %v3bool_var4 "v3bool_var4"
                OpName %v4bool_var5 "v4bool_var5"
                OpName %main "main"
@@ -35,60 +39,68 @@
 %_ptr_Private_int = OpTypePointer Private %int
    %i32_var1 = OpVariable %_ptr_Private_int Private %int_1
    %i32_var2 = OpVariable %_ptr_Private_int Private %int_1
+   %i32_var3 = OpVariable %_ptr_Private_int Private %int_1
        %uint = OpTypeInt 32 0
      %uint_1 = OpConstant %uint 1
 %_ptr_Private_uint = OpTypePointer Private %uint
    %u32_var1 = OpVariable %_ptr_Private_uint Private %uint_1
    %u32_var2 = OpVariable %_ptr_Private_uint Private %uint_1
+   %u32_var3 = OpVariable %_ptr_Private_uint Private %uint_1
      %v3bool = OpTypeVector %bool 3
-         %18 = OpConstantComposite %v3bool %true %true %true
+         %20 = OpConstantComposite %v3bool %true %true %true
 %_ptr_Private_v3bool = OpTypePointer Private %v3bool
-%v3bool_var1 = OpVariable %_ptr_Private_v3bool Private %18
-%v3bool_var2 = OpVariable %_ptr_Private_v3bool Private %18
-%v3bool_var3 = OpVariable %_ptr_Private_v3bool Private %18
+%v3bool_var1 = OpVariable %_ptr_Private_v3bool Private %20
+%v3bool_var2 = OpVariable %_ptr_Private_v3bool Private %20
+%v3bool_var3 = OpVariable %_ptr_Private_v3bool Private %20
       %v3int = OpTypeVector %int 3
-         %24 = OpConstantComposite %v3int %int_1 %int_1 %int_1
+         %26 = OpConstantComposite %v3int %int_1 %int_1 %int_1
 %_ptr_Private_v3int = OpTypePointer Private %v3int
- %v3i32_var1 = OpVariable %_ptr_Private_v3int Private %24
- %v3i32_var2 = OpVariable %_ptr_Private_v3int Private %24
+ %v3i32_var1 = OpVariable %_ptr_Private_v3int Private %26
+ %v3i32_var2 = OpVariable %_ptr_Private_v3int Private %26
+ %v3i32_var3 = OpVariable %_ptr_Private_v3int Private %26
      %v3uint = OpTypeVector %uint 3
-         %29 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+         %32 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
 %_ptr_Private_v3uint = OpTypePointer Private %v3uint
- %v3u32_var1 = OpVariable %_ptr_Private_v3uint Private %29
- %v3u32_var2 = OpVariable %_ptr_Private_v3uint Private %29
-         %33 = OpConstantComposite %v3bool %true %true %true
-%v3bool_var4 = OpVariable %_ptr_Private_v3bool Private %33
+ %v3u32_var1 = OpVariable %_ptr_Private_v3uint Private %32
+ %v3u32_var2 = OpVariable %_ptr_Private_v3uint Private %32
+ %v3u32_var3 = OpVariable %_ptr_Private_v3uint Private %32
+         %37 = OpConstantComposite %v3bool %true %true %true
+%v3bool_var4 = OpVariable %_ptr_Private_v3bool Private %37
      %v4bool = OpTypeVector %bool 4
       %false = OpConstantFalse %bool
-         %37 = OpConstantComposite %v4bool %true %false %true %false
+         %41 = OpConstantComposite %v4bool %true %false %true %false
 %_ptr_Private_v4bool = OpTypePointer Private %v4bool
-%v4bool_var5 = OpVariable %_ptr_Private_v4bool Private %37
+%v4bool_var5 = OpVariable %_ptr_Private_v4bool Private %41
        %void = OpTypeVoid
-         %40 = OpTypeFunction %void
-         %44 = OpConstantNull %bool
-         %45 = OpConstantNull %int
-         %46 = OpConstantNull %uint
-         %47 = OpConstantNull %v3bool
-         %48 = OpConstantNull %v4bool
-         %49 = OpConstantNull %v3int
-         %50 = OpConstantNull %v3uint
-       %main = OpFunction %void None %40
-         %43 = OpLabel
-               OpStore %bool_var1 %44
-               OpStore %bool_var2 %44
-               OpStore %bool_var3 %44
-               OpStore %i32_var1 %45
-               OpStore %i32_var2 %45
-               OpStore %u32_var1 %46
-               OpStore %u32_var2 %46
-               OpStore %v3bool_var1 %47
-               OpStore %v3bool_var2 %47
-               OpStore %v3bool_var3 %47
-               OpStore %v3bool_var4 %47
-               OpStore %v4bool_var5 %48
-               OpStore %v3i32_var1 %49
-               OpStore %v3i32_var2 %49
-               OpStore %v3u32_var1 %50
-               OpStore %v3u32_var2 %50
+         %44 = OpTypeFunction %void
+         %48 = OpConstantNull %bool
+         %49 = OpConstantNull %int
+         %50 = OpConstantNull %uint
+         %51 = OpConstantNull %v3bool
+         %52 = OpConstantNull %v4bool
+         %53 = OpConstantNull %v3int
+         %54 = OpConstantNull %v3uint
+       %main = OpFunction %void None %44
+         %47 = OpLabel
+               OpStore %bool_var1 %48
+               OpStore %bool_var2 %48
+               OpStore %bool_var3 %48
+               OpStore %i32_var1 %49
+               OpStore %i32_var2 %49
+               OpStore %i32_var3 %49
+               OpStore %u32_var1 %50
+               OpStore %u32_var2 %50
+               OpStore %u32_var3 %50
+               OpStore %v3bool_var1 %51
+               OpStore %v3bool_var2 %51
+               OpStore %v3bool_var3 %51
+               OpStore %v3bool_var4 %51
+               OpStore %v4bool_var5 %52
+               OpStore %v3i32_var1 %53
+               OpStore %v3i32_var2 %53
+               OpStore %v3i32_var3 %53
+               OpStore %v3u32_var1 %54
+               OpStore %v3u32_var2 %54
+               OpStore %v3u32_var3 %54
                OpReturn
                OpFunctionEnd
diff --git a/test/types/module_scope_var_conversions.wgsl.expected.wgsl b/test/types/module_scope_var_conversions.wgsl.expected.wgsl
index 457e128..f3b8876 100644
--- a/test/types/module_scope_var_conversions.wgsl.expected.wgsl
+++ b/test/types/module_scope_var_conversions.wgsl.expected.wgsl
@@ -8,10 +8,14 @@
 
 var<private> i32_var2 : i32 = i32(1.0);
 
+var<private> i32_var3 : i32 = i32(true);
+
 var<private> u32_var1 : u32 = u32(1);
 
 var<private> u32_var2 : u32 = u32(1.0);
 
+var<private> u32_var3 : u32 = u32(true);
+
 var<private> v3bool_var1 : vec3<bool> = vec3<bool>(vec3<u32>(1u));
 
 var<private> v3bool_var2 : vec3<bool> = vec3<bool>(vec3<i32>(1));
@@ -22,10 +26,14 @@
 
 var<private> v3i32_var2 : vec3<i32> = vec3<i32>(vec3<f32>(1.0));
 
+var<private> v3i32_var3 : vec3<i32> = vec3<i32>(vec3<bool>(true));
+
 var<private> v3u32_var1 : vec3<u32> = vec3<u32>(vec3<i32>(1));
 
 var<private> v3u32_var2 : vec3<u32> = vec3<u32>(vec3<f32>(1.0));
 
+var<private> v3u32_var3 : vec3<u32> = vec3<u32>(vec3<bool>(true));
+
 var<private> v3bool_var4 : vec3<bool> = vec3<bool>(vec2<bool>(vec2<f32>(123.0)), true);
 
 var<private> v4bool_var5 : vec4<bool> = vec4<bool>(vec2<bool>(vec2<f32>(123.0, 0.0)), vec2<bool>(true, bool(f32(0.0))));
@@ -37,8 +45,10 @@
   bool_var3 = bool();
   i32_var1 = i32();
   i32_var2 = i32();
+  i32_var3 = i32();
   u32_var1 = u32();
   u32_var2 = u32();
+  u32_var3 = u32();
   v3bool_var1 = vec3<bool>();
   v3bool_var2 = vec3<bool>();
   v3bool_var3 = vec3<bool>();
@@ -46,6 +56,8 @@
   v4bool_var5 = vec4<bool>();
   v3i32_var1 = vec3<i32>();
   v3i32_var2 = vec3<i32>();
+  v3i32_var3 = vec3<i32>();
   v3u32_var1 = vec3<u32>();
   v3u32_var2 = vec3<u32>();
+  v3u32_var3 = vec3<u32>();
 }