Import Tint changes from Dawn

Changes:
  - 51719ccc01dc4338ef579d86235c9742546d4956 tint: Fix SPIR-V validation around interpolation decorati... by Ben Clayton <bclayton@google.com>
  - 66d4f6e6fb13dbcdf7d9588eb93008f9149779c3 tint/writer/spirv: Support for F16 type, constructor, and... by Zhaoming Jiang <zhaoming.jiang@intel.com>
GitOrigin-RevId: 51719ccc01dc4338ef579d86235c9742546d4956
Change-Id: Ia0fc3b3b5250f7cfc5088bd80e18930410e23fcc
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/96080
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index b34f66b..11c439d 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -1795,6 +1795,14 @@
         }
     }
 
+    if (type == ast::InterpolationType::kFlat &&
+        !ast::HasAttribute<ast::LocationAttribute>(*attributes)) {
+        // WGSL requires that '@interpolate(flat)' needs to be paired with '@location', however
+        // SPIR-V requires all fragment shader integer Inputs are 'flat'. If the decorations do not
+        // contain a SpvDecorationLocation, then make this perspective.
+        type = ast::InterpolationType::kPerspective;
+    }
+
     // Apply interpolation.
     if (type == ast::InterpolationType::kPerspective &&
         sampling == ast::InterpolationSampling::kNone) {
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index a324bcc..6f84029 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -159,14 +159,16 @@
                                     ast::AttributeList attributes) {
         auto* ast_type = CreateASTTypeFor(ctx, type);
         if (cfg.shader_style == ShaderStyle::kSpirv || cfg.shader_style == ShaderStyle::kGlsl) {
-            // Vulkan requires that integer user-defined fragment inputs are
-            // always decorated with `Flat`.
-            // TODO(crbug.com/tint/1224): Remove this once a flat interpolation
-            // attribute is required for integers.
-            if (type->is_integer_scalar_or_vector() &&
-                ast::HasAttribute<ast::LocationAttribute>(attributes) &&
+            // Vulkan requires that integer user-defined fragment inputs are always decorated with
+            // `Flat`. See:
+            // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/StandaloneSpirv.html#VUID-StandaloneSpirv-Flat-04744
+            // TODO(crbug.com/tint/1224): Remove this once a flat interpolation attribute is
+            // required for integers.
+            if (func_ast->PipelineStage() == ast::PipelineStage::kFragment &&
+                type->is_integer_scalar_or_vector() &&
                 !ast::HasAttribute<ast::InterpolateAttribute>(attributes) &&
-                func_ast->PipelineStage() == ast::PipelineStage::kFragment) {
+                (ast::HasAttribute<ast::LocationAttribute>(attributes) ||
+                 cfg.shader_style == ShaderStyle::kSpirv)) {
                 attributes.push_back(ctx.dst->Interpolate(ast::InterpolationType::kFlat,
                                                           ast::InterpolationSampling::kNone));
             }
@@ -226,14 +228,15 @@
                    const sem::Type* type,
                    ast::AttributeList attributes,
                    const ast::Expression* value) {
-        // Vulkan requires that integer user-defined vertex outputs are
-        // always decorated with `Flat`.
-        // TODO(crbug.com/tint/1224): Remove this once a flat interpolation
-        // attribute is required for integers.
-        if (cfg.shader_style == ShaderStyle::kSpirv && type->is_integer_scalar_or_vector() &&
+        // Vulkan requires that integer user-defined vertex outputs are always decorated with
+        // `Flat`.
+        // TODO(crbug.com/tint/1224): Remove this once a flat interpolation attribute is required
+        // for integers.
+        if (cfg.shader_style == ShaderStyle::kSpirv &&
+            func_ast->PipelineStage() == ast::PipelineStage::kVertex &&
+            type->is_integer_scalar_or_vector() &&
             ast::HasAttribute<ast::LocationAttribute>(attributes) &&
-            !ast::HasAttribute<ast::InterpolateAttribute>(attributes) &&
-            func_ast->PipelineStage() == ast::PipelineStage::kVertex) {
+            !ast::HasAttribute<ast::InterpolateAttribute>(attributes)) {
             attributes.push_back(ctx.dst->Interpolate(ast::InterpolationType::kFlat,
                                                       ast::InterpolationSampling::kNone));
         }
@@ -262,13 +265,17 @@
     /// that will be passed to the original function.
     /// @param param the original function parameter
     void ProcessNonStructParameter(const sem::Parameter* param) {
+        // Do not add interpolation attributes on vertex input
+        bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kVertex;
         // Remove the shader IO attributes from the inner function parameter, and
         // attach them to the new object instead.
         ast::AttributeList attributes;
         for (auto* attr : param->Declaration()->attributes) {
             if (IsShaderIOAttribute(attr)) {
                 ctx.Remove(param->Declaration()->attributes, attr);
-                attributes.push_back(ctx.Clone(attr));
+                if ((do_interpolate || !attr->Is<ast::InterpolateAttribute>())) {
+                    attributes.push_back(ctx.Clone(attr));
+                }
             }
         }
 
@@ -283,6 +290,9 @@
     /// the original function.
     /// @param param the original function parameter
     void ProcessStructParameter(const sem::Parameter* param) {
+        // Do not add interpolation attributes on vertex input
+        bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kVertex;
+
         auto* str = param->Type()->As<sem::Struct>();
 
         // Recreate struct members in the outer entry point and build an initializer
@@ -297,12 +307,6 @@
             auto* member_ast = member->Declaration();
             auto name = ctx.src->Symbols().NameFor(member_ast->symbol);
 
-            // In GLSL, do not add interpolation attributes on vertex input
-            bool do_interpolate = true;
-            if (cfg.shader_style == ShaderStyle::kGlsl &&
-                func_ast->PipelineStage() == ast::PipelineStage::kVertex) {
-                do_interpolate = false;
-            }
             auto attributes = CloneShaderIOAttributes(member_ast->attributes, do_interpolate);
             auto* input_expr = AddInput(name, member->Type(), std::move(attributes));
             inner_struct_values.push_back(input_expr);
@@ -319,12 +323,8 @@
     /// @param inner_ret_type the original function return type
     /// @param original_result the result object produced by the original function
     void ProcessReturnType(const sem::Type* inner_ret_type, Symbol original_result) {
-        bool do_interpolate = true;
-        // In GLSL, do not add interpolation attributes on fragment output
-        if (cfg.shader_style == ShaderStyle::kGlsl &&
-            func_ast->PipelineStage() == ast::PipelineStage::kFragment) {
-            do_interpolate = false;
-        }
+        // Do not add interpolation attributes on fragment output
+        bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kFragment;
         if (auto* str = inner_ret_type->As<sem::Struct>()) {
             for (auto* member : str->Members()) {
                 if (member->Type()->Is<sem::Struct>()) {
diff --git a/src/tint/transform/canonicalize_entry_point_io_test.cc b/src/tint/transform/canonicalize_entry_point_io_test.cc
index f17c5f5..c8af902 100644
--- a/src/tint/transform/canonicalize_entry_point_io_test.cc
+++ b/src/tint/transform/canonicalize_entry_point_io_test.cc
@@ -1978,13 +1978,13 @@
 
 @location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>;
 
-@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
+@location(0) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
 
-@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
+@location(1) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
+@location(2) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
 
-@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
+@location(3) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
 
 struct VertexIn {
   i : i32,
@@ -2108,13 +2108,13 @@
 
 @location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>;
 
-@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
+@location(0) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
 
-@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
+@location(1) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
+@location(2) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
 
-@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
+@location(3) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
 
 fn vert_main_inner(in : VertexIn) -> VertexOut {
   return VertexOut(in.i, in.u, in.vi, in.vu, vec4<f32>());
@@ -2344,7 +2344,7 @@
 }
 
 struct tint_symbol_2 {
-  @location(1) @interpolate(flat)
+  @location(1)
   value : f32,
 }
 
@@ -2397,7 +2397,7 @@
 }
 
 struct tint_symbol_2 {
-  @location(1) @interpolate(flat)
+  @location(1)
   value : f32,
 }
 
@@ -3868,9 +3868,9 @@
 )";
 
     auto* expect = R"(
-@builtin(sample_index) @internal(disable_validation__ignore_storage_class) var<in> sample_index_1 : u32;
+@builtin(sample_index) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> sample_index_1 : u32;
 
-@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<in> mask_in_1 : array<u32, 1u>;
+@builtin(sample_mask) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> mask_in_1 : array<u32, 1u>;
 
 @builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<out> value : array<u32, 1u>;
 
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 833324a..b34fec3 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -379,22 +379,18 @@
 }
 
 bool Builder::GenerateExtension(ast::Extension extension) {
-    /*
-    For each supported extension, push corresponding capability into the builder.
-    For example:
-      if (kind == ast::Extension::Kind::kF16) {
-        push_capability(SpvCapabilityFloat16);
-        push_capability(SpvCapabilityUniformAndStorageBuffer16BitAccess);
-        push_capability(SpvCapabilityStorageBuffer16BitAccess);
-        push_capability(SpvCapabilityStorageInputOutput16);
-      }
-    */
     switch (extension) {
         case ast::Extension::kChromiumExperimentalDP4a:
             push_extension("SPV_KHR_integer_dot_product");
             push_capability(SpvCapabilityDotProductKHR);
             push_capability(SpvCapabilityDotProductInput4x8BitPackedKHR);
             break;
+        case ast::Extension::kF16:
+            push_capability(SpvCapabilityFloat16);
+            push_capability(SpvCapabilityUniformAndStorageBuffer16BitAccess);
+            push_capability(SpvCapabilityStorageBuffer16BitAccess);
+            push_capability(SpvCapabilityStorageInputOutput16);
+            break;
         default:
             return false;
     }
@@ -1354,6 +1350,9 @@
             if (result_type->Is<sem::F32>()) {
                 return GenerateConstantIfNeeded(ScalarConstant::F32(0).AsSpecOp(constant_id));
             }
+            if (result_type->Is<sem::F16>()) {
+                return GenerateConstantIfNeeded(ScalarConstant::F16(0).AsSpecOp(constant_id));
+            }
             if (result_type->Is<sem::Bool>()) {
                 return GenerateConstantIfNeeded(ScalarConstant::Bool(false).AsSpecOp(constant_id));
             }
@@ -1560,22 +1559,23 @@
     auto* from_type = TypeOf(from_expr)->UnwrapRef();
 
     spv::Op op = spv::Op::OpNop;
-    if ((from_type->Is<sem::I32>() && to_type->Is<sem::F32>()) ||
+    if ((from_type->Is<sem::I32>() && to_type->is_float_scalar()) ||
         (from_type->is_signed_integer_vector() && to_type->is_float_vector())) {
         op = spv::Op::OpConvertSToF;
-    } else if ((from_type->Is<sem::U32>() && to_type->Is<sem::F32>()) ||
+    } else if ((from_type->Is<sem::U32>() && to_type->is_float_scalar()) ||
                (from_type->is_unsigned_integer_vector() && to_type->is_float_vector())) {
         op = spv::Op::OpConvertUToF;
-    } else if ((from_type->Is<sem::F32>() && to_type->Is<sem::I32>()) ||
+    } else if ((from_type->is_float_scalar() && to_type->Is<sem::I32>()) ||
                (from_type->is_float_vector() && to_type->is_signed_integer_vector())) {
         op = spv::Op::OpConvertFToS;
-    } else if ((from_type->Is<sem::F32>() && to_type->Is<sem::U32>()) ||
+    } else if ((from_type->is_float_scalar() && to_type->Is<sem::U32>()) ||
                (from_type->is_float_vector() && to_type->is_unsigned_integer_vector())) {
         op = spv::Op::OpConvertFToU;
     } else if ((from_type->Is<sem::Bool>() && to_type->Is<sem::Bool>()) ||
                (from_type->Is<sem::U32>() && to_type->Is<sem::U32>()) ||
                (from_type->Is<sem::I32>() && to_type->Is<sem::I32>()) ||
                (from_type->Is<sem::F32>() && to_type->Is<sem::F32>()) ||
+               (from_type->Is<sem::F16>() && to_type->Is<sem::F16>()) ||
                (from_type->Is<sem::Vector>() && (from_type == to_type))) {
         return val_id;
     } else if ((from_type->Is<sem::I32>() && to_type->Is<sem::U32>()) ||
@@ -1608,6 +1608,9 @@
         if (to_elem_type->Is<sem::F32>()) {
             zero_id = GenerateConstantIfNeeded(ScalarConstant::F32(0));
             one_id = GenerateConstantIfNeeded(ScalarConstant::F32(1));
+        } else if (to_elem_type->Is<sem::F16>()) {
+            zero_id = GenerateConstantIfNeeded(ScalarConstant::F16(0));
+            one_id = GenerateConstantIfNeeded(ScalarConstant::F16(1));
         } else if (to_elem_type->Is<sem::U32>()) {
             zero_id = GenerateConstantIfNeeded(ScalarConstant::U32(0));
             one_id = GenerateConstantIfNeeded(ScalarConstant::U32(1));
@@ -1691,7 +1694,9 @@
                     constant.value.f32 = static_cast<float>(f->value);
                     return;
                 case ast::FloatLiteralExpression::Suffix::kH:
-                    error_ = "Type f16 is not completely implemented yet";
+                    constant.kind = ScalarConstant::Kind::kF16;
+                    constant.value.f16 = {f16(static_cast<float>(f->value)).BitsRepresentation()};
+                    return;
             }
         },
         [&](Default) { error_ = "unknown literal type"; });
@@ -1750,6 +1755,10 @@
             auto val = constant->As<f32>();
             return GenerateConstantIfNeeded(ScalarConstant::F32(val.value));
         },
+        [&](const sem::F16*) {
+            auto val = constant->As<f16>();
+            return GenerateConstantIfNeeded(ScalarConstant::F16(val.value));
+        },
         [&](const sem::I32*) {
             auto val = constant->As<i32>();
             return GenerateConstantIfNeeded(ScalarConstant::I32(val.value));
@@ -1788,6 +1797,10 @@
             type_id = GenerateTypeIfNeeded(builder_.create<sem::F32>());
             break;
         }
+        case ScalarConstant::Kind::kF16: {
+            type_id = GenerateTypeIfNeeded(builder_.create<sem::F16>());
+            break;
+        }
         case ScalarConstant::Kind::kBool: {
             type_id = GenerateTypeIfNeeded(builder_.create<sem::Bool>());
             break;
@@ -1822,6 +1835,12 @@
                       {Operand(type_id), result, Operand(constant.value.f32)});
             break;
         }
+        case ScalarConstant::Kind::kF16: {
+            push_type(
+                constant.is_spec_op ? spv::Op::OpSpecConstant : spv::Op::OpConstant,
+                {Operand(type_id), result, U32Operand(constant.value.f16.bits_representation)});
+            break;
+        }
         case ScalarConstant::Kind::kBool: {
             if (constant.value.b) {
                 push_type(
@@ -3795,9 +3814,8 @@
                 return true;
             },
             [&](const sem::F16*) {
-                // Should be `push_type(spv::Op::OpTypeFloat, {result, Operand(16u)});`
-                error_ = "Type f16 is not completely implemented yet.";
-                return false;
+                push_type(spv::Op::OpTypeFloat, {result, Operand(16u)});
+                return true;
             },
             [&](const sem::I32*) {
                 push_type(spv::Op::OpTypeInt, {result, Operand(32u), Operand(1u)});
diff --git a/src/tint/writer/spirv/builder_constructor_expression_test.cc b/src/tint/writer/spirv/builder_constructor_expression_test.cc
index d8cb809..7fc7ac6 100644
--- a/src/tint/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_constructor_expression_test.cc
@@ -212,6 +212,23 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_F16_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = Construct<f16>(2_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 2u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Bool_Literal) {
     auto* cast = vec2<bool>(true);
     WrapInFunction(cast);
@@ -270,6 +287,25 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F16_Literal) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(2_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32_F32) {
     auto* var = Decl(Var("x", ty.f32(), Expr(2_f)));
     auto* cast = vec2<f32>("x", "x");
@@ -295,6 +331,33 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F16_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2_h)));
+    auto* cast = vec2<f16>("x", "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 9u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+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 = OpLoad %1 %3
+%9 = OpCompositeConstruct %6 %7 %8
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F32_F32_Const) {
     auto* cast = vec2<f32>(1_f, 2_f);
     WrapInFunction(cast);
@@ -313,7 +376,27 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_F16_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(1_h, 2_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 5u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_F32_With_Vec2) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(1_f, 2_f)));
     auto* cast = vec2<f32>("x");
     WrapInFunction(var, cast);
@@ -338,7 +421,34 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec2_With_Vec2_Const) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_F16_With_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(1_h, 2_h)));
+    auto* cast = vec2<f16>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 10u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%10 = OpLoad %1 %6
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_F32_With_Vec2_Const) {
     auto* cast = vec2<f32>(vec2<f32>(1_f, 2_f));
     WrapInFunction(cast);
 
@@ -356,6 +466,26 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec2_F16_With_Vec2_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(vec2<f16>(1_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 5u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F32) {
     auto* var = Decl(Var("x", ty.f32(), Expr(2_f)));
     auto* cast = vec3<f32>("x", "x", "x");
@@ -382,6 +512,34 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2_h)));
+    auto* cast = vec3<f16>("x", "x", "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 10u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%6 = OpTypeVector %1 3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%7 = OpLoad %1 %3
+%8 = OpLoad %1 %3
+%9 = OpLoad %1 %3
+%10 = OpCompositeConstruct %6 %7 %8 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F32_Const) {
     auto* cast = vec3<f32>(1_f, 2_f, 3_f);
     WrapInFunction(cast);
@@ -401,6 +559,27 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(1_h, 2_h, 3_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Bool) {
     auto* var = Decl(Var("x", ty.bool_(), Expr(true)));
     auto* cast = vec3<bool>("x", "x", "x");
@@ -471,6 +650,34 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F16_F16_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2_h)));
+    auto* cast = vec3<f16>("x", "x", "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 10u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%6 = OpTypeVector %1 3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%7 = OpLoad %1 %3
+%8 = OpLoad %1 %3
+%9 = OpLoad %1 %3
+%10 = OpCompositeConstruct %6 %7 %8 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F32_F32_F32_Const) {
     auto* cast = vec3<f32>(1_f, 2_f, 3_f);
     WrapInFunction(cast);
@@ -490,6 +697,27 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F16_F16_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(1_h, 2_h, 3_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F32_Vec2) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(2_f, 3_f)));
     auto* cast = vec3<f32>(1_f, "x");
@@ -520,6 +748,38 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F16_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(2_h, 3_h)));
+    auto* cast = vec3<f16>(1_h, "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 14u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstant %2 0x1.8p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+%9 = OpTypeVector %2 3
+%10 = OpConstant %2 0x1p+0
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%11 = OpLoad %1 %6
+%12 = OpCompositeExtract %2 %11 0
+%13 = OpCompositeExtract %2 %11 1
+%14 = OpCompositeConstruct %9 %10 %12 %13
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F32_Vec2_Const) {
     auto* cast = vec3<f32>(1_f, vec2<f32>(2_f, 3_f));
     WrapInFunction(cast);
@@ -539,6 +799,27 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_F16_Vec2_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(1_h, vec2<f16>(2_h, 3_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec2_F32) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(1_f, 2_f)));
     auto* cast = vec3<f32>("x", 3_f);
@@ -569,6 +850,38 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec2_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(1_h, 2_h)));
+    auto* cast = vec3<f16>("x", 3_h);
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 14u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+%9 = OpTypeVector %2 3
+%13 = OpConstant %2 0x1.8p+1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%10 = OpLoad %1 %6
+%11 = OpCompositeExtract %2 %10 0
+%12 = OpCompositeExtract %2 %10 1
+%14 = OpCompositeConstruct %9 %11 %12 %13
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec2_F32_Const) {
     auto* cast = vec3<f32>(vec2<f32>(1_f, 2_f), 3_f);
     WrapInFunction(cast);
@@ -588,7 +901,28 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec2_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(vec2<f16>(1_h, 2_h), 3_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_F32_With_Vec3) {
     auto* var = Decl(Var("x", ty.vec3<f32>(), vec3<f32>(1_f, 2_f, 3_f)));
     auto* cast = vec3<f32>("x");
     WrapInFunction(var, cast);
@@ -614,7 +948,35 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec3_With_Vec3_Const) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_F16_With_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec3<f16>(), vec3<f16>(1_h, 2_h, 3_h)));
+    auto* cast = vec3<f16>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 11u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Function %1
+%9 = OpConstantNull %1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %7 %6
+%11 = OpLoad %1 %7
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_F32_With_Vec3_Const) {
     auto* cast = vec3<f32>(vec3<f32>(1_f, 2_f, 3_f));
     WrapInFunction(cast);
 
@@ -633,6 +995,27 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec3_F16_With_Vec3_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(vec3<f16>(1_h, 2_h, 3_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Bool) {
     auto* var = Decl(Var("x", ty.bool_(), Expr(true)));
     auto* cast = vec4<bool>("x");
@@ -698,6 +1081,32 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2_h)));
+    auto* cast = vec4<f16>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 8u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%6 = OpTypeVector %1 4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%7 = OpLoad %1 %3
+%8 = OpCompositeConstruct %6 %7 %7 %7 %7
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_Const) {
     auto* cast = vec4<f32>(2_f);
     WrapInFunction(cast);
@@ -715,6 +1124,25 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_F32_F32_F32) {
     auto* var = Decl(Var("x", ty.f32(), Expr(2_f)));
     auto* cast = vec4<f32>("x", "x", "x", "x");
@@ -742,6 +1170,35 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_F16_F16_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2_h)));
+    auto* cast = vec4<f16>("x", "x", "x", "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 11u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%6 = OpTypeVector %1 4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%7 = OpLoad %1 %3
+%8 = OpLoad %1 %3
+%9 = OpLoad %1 %3
+%10 = OpLoad %1 %3
+%11 = OpCompositeConstruct %6 %7 %8 %9 %10
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_F32_F32_F32_Const) {
     auto* cast = vec4<f32>(1_f, 2_f, 3_f, 4_f);
     WrapInFunction(cast);
@@ -762,6 +1219,28 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_F16_F16_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(1_h, 2_h, 3_h, 4_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 7u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstant %2 0x1p+2
+%7 = OpConstantComposite %1 %3 %4 %5 %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_F32_Vec2) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(1_f, 2_f)));
     auto* cast = vec4<f32>(1_f, 2_f, "x");
@@ -791,6 +1270,37 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_F16_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(1_h, 2_h)));
+    auto* cast = vec4<f16>(1_h, 2_h, "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 13u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+%9 = OpTypeVector %2 4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%10 = OpLoad %1 %6
+%11 = OpCompositeExtract %2 %10 0
+%12 = OpCompositeExtract %2 %10 1
+%13 = OpCompositeConstruct %9 %3 %4 %11 %12
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_F32_Vec2_Const) {
     auto* cast = vec4<f32>(1_f, 2_f, vec2<f32>(3_f, 4_f));
     WrapInFunction(cast);
@@ -811,6 +1321,28 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_F16_Vec2_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(1_h, 2_h, vec2<f16>(3_h, 4_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 7u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstant %2 0x1p+2
+%7 = OpConstantComposite %1 %3 %4 %5 %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_Vec2_F32) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(2_f, 3_f)));
     auto* cast = vec4<f32>(1_f, "x", 4_f);
@@ -842,6 +1374,39 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_Vec2_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(2_h, 3_h)));
+    auto* cast = vec4<f16>(1_h, "x", 4_h);
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 15u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstant %2 0x1.8p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+%9 = OpTypeVector %2 4
+%10 = OpConstant %2 0x1p+0
+%14 = OpConstant %2 0x1p+2
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%11 = OpLoad %1 %6
+%12 = OpCompositeExtract %2 %11 0
+%13 = OpCompositeExtract %2 %11 1
+%15 = OpCompositeConstruct %9 %10 %12 %13 %14
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_Vec2_F32_Const) {
     auto* cast = vec4<f32>(1_f, vec2<f32>(2_f, 3_f), 4_f);
     WrapInFunction(cast);
@@ -862,6 +1427,28 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_Vec2_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(1_h, vec2<f16>(2_h, 3_h), 4_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 7u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstant %2 0x1p+2
+%7 = OpConstantComposite %1 %3 %4 %5 %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec2_F32_F32) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(1_f, 2_f)));
     auto* cast = vec4<f32>("x", 3_f, 4_f);
@@ -893,6 +1480,39 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec2_F16_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(1_h, 2_h)));
+    auto* cast = vec4<f16>("x", 3_h, 4_h);
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 15u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+%9 = OpTypeVector %2 4
+%13 = OpConstant %2 0x1.8p+1
+%14 = OpConstant %2 0x1p+2
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%10 = OpLoad %1 %6
+%11 = OpCompositeExtract %2 %10 0
+%12 = OpCompositeExtract %2 %10 1
+%15 = OpCompositeConstruct %9 %11 %12 %13 %14
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec2_F32_F32_Const) {
     auto* cast = vec4<f32>(vec2<f32>(1_f, 2_f), 3_f, 4_f);
     WrapInFunction(cast);
@@ -913,7 +1533,29 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec2_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec2_F16_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec2<f16>(1_h, 2_h), 3_h, 4_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 7u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstant %2 0x1p+2
+%7 = OpConstantComposite %1 %3 %4 %5 %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_F32_With_Vec2_Vec2) {
     auto* var = Decl(Var("x", ty.vec2<f32>(), vec2<f32>(1_f, 2_f)));
     auto* cast = vec4<f32>("x", "x");
     WrapInFunction(var, cast);
@@ -945,7 +1587,41 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec2_Vec2_Const) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_F16_With_Vec2_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec2<f16>(), vec2<f16>(1_h, 2_h)));
+    auto* cast = vec4<f16>("x", "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 16u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4
+%7 = OpTypePointer Function %1
+%8 = OpConstantNull %1
+%9 = OpTypeVector %2 4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %6 %5
+%10 = OpLoad %1 %6
+%11 = OpCompositeExtract %2 %10 0
+%12 = OpCompositeExtract %2 %10 1
+%13 = OpLoad %1 %6
+%14 = OpCompositeExtract %2 %13 0
+%15 = OpCompositeExtract %2 %13 1
+%16 = OpCompositeConstruct %9 %11 %12 %14 %15
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_F32_With_Vec2_Vec2_Const) {
     auto* cast = vec4<f32>(vec2<f32>(1_f, 2_f), vec2<f32>(1_f, 2_f));
     WrapInFunction(cast);
 
@@ -963,6 +1639,26 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_F16_With_Vec2_Vec2_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec2<f16>(1_h, 2_h), vec2<f16>(1_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 5u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstantComposite %1 %3 %4 %3 %4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_Vec3) {
     auto* var = Decl(Var("x", ty.vec3<f32>(), vec3<f32>(2_f, 2_f, 2_f)));
     auto* cast = vec4<f32>(2_f, "x");
@@ -992,6 +1688,37 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec3<f16>(), vec3<f16>(2_h, 2_h, 2_h)));
+    auto* cast = vec4<f16>(2_h, "x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 13u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3
+%6 = OpTypePointer Function %1
+%7 = OpConstantNull %1
+%8 = OpTypeVector %2 4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %5 %4
+%9 = OpLoad %1 %5
+%10 = OpCompositeExtract %2 %9 0
+%11 = OpCompositeExtract %2 %9 1
+%12 = OpCompositeExtract %2 %9 2
+%13 = OpCompositeConstruct %8 %3 %10 %11 %12
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F32_Vec3_Const) {
     auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
     WrapInFunction(cast);
@@ -1009,6 +1736,25 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_F16_Vec3_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h, vec3<f16>(2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec3_F32) {
     auto* var = Decl(Var("x", ty.vec3<f32>(), vec3<f32>(2_f, 2_f, 2_f)));
     auto* cast = vec4<f32>("x", 2_f);
@@ -1038,6 +1784,37 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec3_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.vec3<f16>(), vec3<f16>(2_h, 2_h, 2_h)));
+    auto* cast = vec4<f16>("x", 2_h);
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var));
+    EXPECT_EQ(b.GenerateExpression(cast), 13u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3
+%6 = OpTypePointer Function %1
+%7 = OpConstantNull %1
+%8 = OpTypeVector %2 4
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %5 %4
+%9 = OpLoad %1 %5
+%10 = OpCompositeExtract %2 %9 0
+%11 = OpCompositeExtract %2 %9 1
+%12 = OpCompositeExtract %2 %9 2
+%13 = OpCompositeConstruct %8 %10 %11 %12 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec3_F32_Const) {
     auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
     WrapInFunction(cast);
@@ -1055,7 +1832,26 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_With_Vec3_F16_Const) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec3<f16>(2_h, 2_h, 2_h), 2_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_F32_With_Vec4) {
     auto* value = vec4<f32>(2_f, 2_f, 2_f, 2_f);
     auto* cast = vec4<f32>(value);
     WrapInFunction(cast);
@@ -1073,6 +1869,26 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Vec4_F16_With_Vec4) {
+    Enable(ast::Extension::kF16);
+
+    auto* value = vec4<f16>(2_h, 2_h, 2_h, 2_h);
+    auto* cast = vec4<f16>(value);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_F32_With_F32) {
     auto* ctor = Construct<f32>(2_f);
     GlobalConst("g", ty.f32(), ctor);
@@ -1094,6 +1910,29 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_F16_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* ctor = Construct<f16>(2_h);
+    GlobalConst("g", ty.f16(), ctor);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 16
+%6 = OpConstant %5 0x1p+1
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %7 %6
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_F32_With_F32) {
     auto* ctor = Construct<f32>(2_f);
     GlobalVar("g", ty.f32(), ast::StorageClass::kPrivate, ctor);
@@ -1111,6 +1950,25 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_F16_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* ctor = Construct<f16>(2_h);
+    GlobalVar("g", ty.f16(), ast::StorageClass::kPrivate, ctor);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1p+1
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_U32_With_F32) {
     auto* ctor = Construct<u32>(1.5_f);
     GlobalConst("g", ty.u32(), ctor);
@@ -1132,6 +1990,29 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_U32_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* ctor = Construct<u32>(1.5_h);
+    GlobalConst("g", ty.u32(), ctor);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 1
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %7 %6
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_U32_With_F32) {
     auto* ctor = Construct<u32>(1.5_f);
     GlobalVar("g", ty.u32(), ast::StorageClass::kPrivate, ctor);
@@ -1149,6 +2030,25 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_U32_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* ctor = Construct<u32>(1.5_h);
+    GlobalVar("g", ty.u32(), ast::StorageClass::kPrivate, ctor);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpConstant %1 1
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_F32) {
     auto* cast = vec2<f32>(2_f);
     GlobalConst("g", ty.vec2<f32>(), cast);
@@ -1172,6 +2072,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(2_h);
+    GlobalConst("g", ty.vec2<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_F32) {
     auto* cast = vec2<f32>(2_f);
     auto* g = GlobalVar("g", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1188,7 +2113,25 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(2_h);
+    auto* g = GlobalVar("g", ty.vec2<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_F32_With_Vec2) {
     auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
     GlobalConst("g", ty.vec2<f32>(), cast);
     WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
@@ -1211,7 +2154,32 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_F16_With_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(vec2<f16>(2_h, 2_h));
+    GlobalConst("g", ty.vec2<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_F32_With_Vec2) {
     auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
     GlobalVar("a", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
 
@@ -1231,7 +2199,29 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_F16_With_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec2<f16>(vec2<f16>(2_h, 2_h));
+    GlobalVar("a", ty.vec2<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+)");
+
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_F32_With_Vec3) {
     auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
     GlobalConst("g", ty.vec3<f32>(), cast);
     WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
@@ -1254,7 +2244,32 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_F16_With_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(vec3<f16>(2_h, 2_h, 2_h));
+    GlobalConst("g", ty.vec3<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_F32_With_Vec3) {
     auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
     GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
 
@@ -1274,7 +2289,29 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_F16_With_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(vec3<f16>(2_h, 2_h, 2_h));
+    GlobalVar("a", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+)");
+
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_F32_With_Vec4) {
     auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
     GlobalConst("g", ty.vec4<f32>(), cast);
     WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
@@ -1296,7 +2333,33 @@
 )");
     Validate(b);
 }
-TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec4) {
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_F16_With_Vec4) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec4<f16>(2_h, 2_h, 2_h, 2_h));
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_F32_With_Vec4) {
     auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
     GlobalVar("a", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
@@ -1316,6 +2379,28 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_F16_With_Vec4) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec4<f16>(2_h, 2_h, 2_h, 2_h));
+    GlobalVar("a", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+)");
+
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F32) {
     auto* cast = vec3<f32>(2_f);
     GlobalConst("g", ty.vec3<f32>(), cast);
@@ -1339,6 +2424,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(2_h);
+    GlobalConst("g", ty.vec3<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32) {
     auto* cast = vec3<f32>(2_f);
     auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1355,6 +2465,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(2_h);
+    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F32_Vec2) {
     auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
     GlobalConst("g", ty.vec3<f32>(), cast);
@@ -1378,6 +2506,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F16_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(2_h, vec2<f16>(2_h, 2_h));
+    GlobalConst("g", ty.vec3<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32_Vec2) {
     auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
     auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1394,6 +2547,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F16_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(2_h, vec2<f16>(2_h, 2_h));
+    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec2_F32) {
     auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
     GlobalConst("g", ty.vec3<f32>(), cast);
@@ -1417,6 +2588,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec2_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(vec2<f16>(2_h, 2_h), 2_h);
+    GlobalConst("g", ty.vec3<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec2_F32) {
     auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
     auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1433,6 +2629,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec2_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec3<f16>(vec2<f16>(2_h, 2_h), 2_h);
+    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32) {
     auto* cast = vec4<f32>(2_f);
     GlobalConst("g", ty.vec4<f32>(), cast);
@@ -1456,6 +2670,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h);
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32) {
     auto* cast = vec4<f32>(2_f);
     auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1472,6 +2711,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_F32_Vec2) {
     auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
     GlobalConst("g", ty.vec4<f32>(), cast);
@@ -1495,6 +2752,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F16_F16_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h, 2_h, vec2<f16>(2_h, 2_h));
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_F32_Vec2) {
     auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
     auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1511,6 +2793,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F16_F16_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h, 2_h, vec2<f16>(2_h, 2_h));
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_Vec2_F32) {
     auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
     GlobalConst("g", ty.vec4<f32>(), cast);
@@ -1534,6 +2834,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F16_Vec2_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h, vec2<f16>(2_h, 2_h), 2_h);
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec2_F32) {
     auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
     auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1550,6 +2875,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F16_Vec2_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h, vec2<f16>(2_h, 2_h), 2_h);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_F32_F32) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
     GlobalConst("g", ty.vec4<f32>(), cast);
@@ -1573,6 +2916,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_F16_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec2<f16>(2_h, 2_h), 2_h, 2_h);
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_F32_F32) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
     auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1589,7 +2957,25 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_F16_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec2<f16>(2_h, 2_h), 2_h, 2_h);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_F32_With_Vec2_Vec2) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
     GlobalConst("g", ty.vec4<f32>(), cast);
     WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
@@ -1612,7 +2998,32 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_F16_With_Vec2_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h));
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_F32_With_Vec2_Vec2) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
     auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
@@ -1628,6 +3039,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_F16_With_Vec2_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h));
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_Vec3) {
     auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
     GlobalConst("g", ty.vec4<f32>(), cast);
@@ -1667,6 +3096,24 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F16_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(2_h, vec3<f16>(2_h, 2_h, 2_h));
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec3_F32) {
     auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
     GlobalConst("g", ty.vec4<f32>(), cast);
@@ -1690,6 +3137,31 @@
     Validate(b);
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec3_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec3<f16>(2_h, 2_h, 2_h), 2_h);
+    GlobalConst("g", ty.vec4<f16>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 0x1p+1
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec3_F32) {
     auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
     auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
@@ -1706,7 +3178,25 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat2x2_With_Vec2_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec3_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = vec4<f16>(vec3<f16>(2_h, 2_h, 2_h), 2_h);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 0x1p+1
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat2x2_F32_With_Vec2_Vec2) {
     auto* cast = mat2x2<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
     WrapInFunction(cast);
 
@@ -1724,7 +3214,27 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat3x2_With_Vec2_Vec2_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat2x2_F16_With_Vec2_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat2x2<f16>(vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 2
+%1 = OpTypeMatrix %2 2
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4
+%6 = OpConstantComposite %1 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat3x2_F32_With_Vec2_Vec2_Vec2) {
     auto* cast = mat3x2<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
     WrapInFunction(cast);
 
@@ -1742,7 +3252,27 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat4x2_With_Vec2_Vec2_Vec2_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat3x2_F16_With_Vec2_Vec2_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat3x2<f16>(vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 2
+%1 = OpTypeMatrix %2 3
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4
+%6 = OpConstantComposite %1 %5 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat4x2_F32_With_Vec2_Vec2_Vec2_Vec2) {
     auto* cast = mat4x2<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f),
                              vec2<f32>(2_f, 2_f));
     WrapInFunction(cast);
@@ -1761,7 +3291,28 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat2x3_With_Vec3_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat4x2_F16_With_Vec2_Vec2_Vec2_Vec2) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat4x2<f16>(vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h),
+                             vec2<f16>(2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 2
+%1 = OpTypeMatrix %2 4
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4
+%6 = OpConstantComposite %1 %5 %5 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat2x3_F32_With_Vec3_Vec3) {
     auto* cast = mat2x3<f32>(vec3<f32>(2_f, 2_f, 2_f), vec3<f32>(2_f, 2_f, 2_f));
     WrapInFunction(cast);
 
@@ -1779,7 +3330,27 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat3x3_With_Vec3_Vec3_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat2x3_F16_With_Vec3_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat2x3<f16>(vec3<f16>(2_h, 2_h, 2_h), vec3<f16>(2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 3
+%1 = OpTypeMatrix %2 2
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4 %4
+%6 = OpConstantComposite %1 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat3x3_F32_With_Vec3_Vec3_Vec3) {
     auto* cast =
         mat3x3<f32>(vec3<f32>(2_f, 2_f, 2_f), vec3<f32>(2_f, 2_f, 2_f), vec3<f32>(2_f, 2_f, 2_f));
     WrapInFunction(cast);
@@ -1798,7 +3369,28 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat4x3_With_Vec3_Vec3_Vec3_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat3x3_F16_With_Vec3_Vec3_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast =
+        mat3x3<f16>(vec3<f16>(2_h, 2_h, 2_h), vec3<f16>(2_h, 2_h, 2_h), vec3<f16>(2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 3
+%1 = OpTypeMatrix %2 3
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4 %4
+%6 = OpConstantComposite %1 %5 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat4x3_F32_With_Vec3_Vec3_Vec3_Vec3) {
     auto* cast = mat4x3<f32>(vec3<f32>(2_f, 2_f, 2_f), vec3<f32>(2_f, 2_f, 2_f),
                              vec3<f32>(2_f, 2_f, 2_f), vec3<f32>(2_f, 2_f, 2_f));
     WrapInFunction(cast);
@@ -1817,7 +3409,28 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat2x4_With_Vec4_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat4x3_F16_With_Vec3_Vec3_Vec3_Vec3) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat4x3<f16>(vec3<f16>(2_h, 2_h, 2_h), vec3<f16>(2_h, 2_h, 2_h),
+                             vec3<f16>(2_h, 2_h, 2_h), vec3<f16>(2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 3
+%1 = OpTypeMatrix %2 4
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4 %4
+%6 = OpConstantComposite %1 %5 %5 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat2x4_F32_With_Vec4_Vec4) {
     auto* cast = mat2x4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f), vec4<f32>(2_f, 2_f, 2_f, 2_f));
     WrapInFunction(cast);
 
@@ -1835,7 +3448,27 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat3x4_With_Vec4_Vec4_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat2x4_F16_With_Vec4_Vec4) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat2x4<f16>(vec4<f16>(2_h, 2_h, 2_h, 2_h), vec4<f16>(2_h, 2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 4
+%1 = OpTypeMatrix %2 2
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4 %4 %4
+%6 = OpConstantComposite %1 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat3x4_F32_With_Vec4_Vec4_Vec4) {
     auto* cast = mat3x4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f), vec4<f32>(2_f, 2_f, 2_f, 2_f),
                              vec4<f32>(2_f, 2_f, 2_f, 2_f));
     WrapInFunction(cast);
@@ -1854,7 +3487,28 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Mat4x4_With_Vec4_Vec4_Vec4_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_Mat3x4_F16_With_Vec4_Vec4_Vec4) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat3x4<f16>(vec4<f16>(2_h, 2_h, 2_h, 2_h), vec4<f16>(2_h, 2_h, 2_h, 2_h),
+                             vec4<f16>(2_h, 2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 4
+%1 = OpTypeMatrix %2 3
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4 %4 %4
+%6 = OpConstantComposite %1 %5 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Mat4x4_F32_With_Vec4_Vec4_Vec4_Vec4) {
     auto* cast = mat4x4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f), vec4<f32>(2_f, 2_f, 2_f, 2_f),
                              vec4<f32>(2_f, 2_f, 2_f, 2_f), vec4<f32>(2_f, 2_f, 2_f, 2_f));
     WrapInFunction(cast);
@@ -1873,6 +3527,27 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Mat4x4_F16_With_Vec4_Vec4_Vec4_Vec4) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = mat4x4<f16>(vec4<f16>(2_h, 2_h, 2_h, 2_h), vec4<f16>(2_h, 2_h, 2_h, 2_h),
+                             vec4<f16>(2_h, 2_h, 2_h, 2_h), vec4<f16>(2_h, 2_h, 2_h, 2_h));
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 4
+%1 = OpTypeMatrix %2 4
+%4 = OpConstant %3 0x1p+1
+%5 = OpConstantComposite %2 %4 %4 %4 %4
+%6 = OpConstantComposite %1 %5 %5 %5 %5
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Array_5_F32) {
     auto* cast = array<f32, 5>(2_f, 2_f, 2_f, 2_f, 2_f);
     WrapInFunction(cast);
@@ -1891,7 +3566,27 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_Array_5_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* cast = array<f16, 5>(2_h, 2_h, 2_h, 2_h, 2_h);
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(cast), 6u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%3 = OpTypeInt 32 0
+%4 = OpConstant %3 5
+%1 = OpTypeArray %2 %4
+%5 = OpConstant %2 0x1p+1
+%6 = OpConstantComposite %1 %5 %5 %5 %5 %5
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3_F32) {
     auto* first = vec3<f32>(1_f, 2_f, 3_f);
     auto* second = vec3<f32>(1_f, 2_f, 3_f);
     auto* t = Construct(ty.array(ty.vec3<f32>(), 2_u), first, second);
@@ -1913,6 +3608,30 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* first = vec3<f16>(1_h, 2_h, 3_h);
+    auto* second = vec3<f16>(1_h, 2_h, 3_h);
+    auto* t = Construct(ty.array(ty.vec3<f16>(), 2_u), first, second);
+    WrapInFunction(t);
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateExpression(t), 10u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 3
+%4 = OpTypeInt 32 0
+%5 = OpConstant %4 2
+%1 = OpTypeArray %2 %5
+%6 = OpConstant %3 0x1p+0
+%7 = OpConstant %3 0x1p+1
+%8 = OpConstant %3 0x1.8p+1
+%9 = OpConstantComposite %2 %6 %7 %8
+%10 = OpConstantComposite %1 %9 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, CommonInitializer_TwoVectors) {
     auto* v1 = vec3<f32>(2_f, 2_f, 2_f);
     auto* v2 = vec3<f32>(2_f, 2_f, 2_f);
@@ -2030,6 +3749,25 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* t = Construct<f16>();
+
+    WrapInFunction(t);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+
+    EXPECT_EQ(b.GenerateExpression(t), 2u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstantNull %1
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_I32) {
     auto* t = Construct<i32>();
 
@@ -2099,7 +3837,7 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Matrix) {
+TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Matrix_F32) {
     auto* t = mat4x2<f32>();
 
     WrapInFunction(t);
@@ -2118,6 +3856,27 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Matrix_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* t = mat4x2<f16>();
+
+    WrapInFunction(t);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+
+    EXPECT_EQ(b.GenerateExpression(t), 4u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 2
+%1 = OpTypeMatrix %2 4
+%4 = OpConstantNull %1
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Array) {
     auto* t = array<i32, 2>();
 
@@ -2228,6 +3987,32 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_F16_To_I32) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2.4_h)));
+    auto* cast = Construct<i32>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1.33p+1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%7 = OpTypeInt 32 1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%8 = OpLoad %1 %3
+%6 = OpConvertFToS %7 %8
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_F32_To_U32) {
     auto* var = Decl(Var("x", ty.f32(), Expr(2.4_f)));
     auto* cast = Construct<u32>("x");
@@ -2252,6 +4037,32 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_F16_To_U32) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.f16(), Expr(2.4_h)));
+    auto* cast = Construct<u32>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1.33p+1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%7 = OpTypeInt 32 0
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%8 = OpLoad %1 %3
+%6 = OpConvertFToU %7 %8
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_I32_To_F32) {
     auto* var = Decl(Var("x", ty.i32(), Expr(2_i)));
     auto* cast = Construct<f32>("x");
@@ -2276,6 +4087,32 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_I32_To_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.i32(), Expr(2_i)));
+    auto* cast = Construct<f16>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstant %1 2
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%7 = OpTypeFloat 16
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%8 = OpLoad %1 %3
+%6 = OpConvertSToF %7 %8
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_U32_To_F32) {
     auto* var = Decl(Var("x", ty.u32(), Expr(2_u)));
     auto* cast = Construct<f32>("x");
@@ -2300,6 +4137,32 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_U32_To_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Decl(Var("x", ty.u32(), Expr(2_u)));
+    auto* cast = Construct<f16>("x");
+    WrapInFunction(var, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateStatement(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpConstant %1 2
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+%7 = OpTypeFloat 16
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+%8 = OpLoad %1 %3
+%6 = OpConvertUToF %7 %8
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_I32) {
     auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
 
@@ -2352,6 +4215,34 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F16_to_I32) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+
+    auto* cast = vec3<i32>("i");
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 16
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeInt 32 1
+%7 = OpTypeVector %8 3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%9 = OpLoad %3 %1
+%6 = OpConvertFToS %7 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_U32) {
     auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
 
@@ -2404,6 +4295,34 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F16_to_U32) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+
+    auto* cast = vec3<u32>("i");
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 16
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeInt 32 0
+%7 = OpTypeVector %8 3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%9 = OpLoad %3 %1
+%6 = OpConvertFToU %7 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_F32) {
     auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
 
@@ -2430,6 +4349,34 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+
+    auto* cast = vec3<f16>("i");
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeFloat 16
+%7 = OpTypeVector %8 3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%9 = OpLoad %3 %1
+%6 = OpConvertSToF %7 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_F32) {
     auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
 
@@ -2456,6 +4403,34 @@
 )");
 }
 
+TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+
+    auto* cast = vec3<f16>("i");
+    WrapInFunction(cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+    EXPECT_EQ(b.GenerateExpression(cast), 6u) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeFloat 16
+%7 = OpTypeVector %8 3
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%9 = OpLoad %3 %1
+%6 = OpConvertUToF %7 %9
+)");
+}
+
 TEST_F(SpvBuilderConstructorTest, IsConstructorConst_GlobalVectorWithAllConstConstructors) {
     // vec3<f32>(1.0, 2.0, 3.0)  -> true
     auto* t = vec3<f32>(1_f, 2_f, 3_f);
diff --git a/src/tint/writer/spirv/builder_entry_point_test.cc b/src/tint/writer/spirv/builder_entry_point_test.cc
index 8cbf2cc..8e9d857 100644
--- a/src/tint/writer/spirv/builder_entry_point_test.cc
+++ b/src/tint/writer/spirv/builder_entry_point_test.cc
@@ -311,9 +311,12 @@
 
     // Make sure we generate the SampleRateShading capability.
     EXPECT_EQ(DumpInstructions(b.capabilities()),
-              "OpCapability Shader\n"
-              "OpCapability SampleRateShading\n");
-    EXPECT_EQ(DumpInstructions(b.annots()), "OpDecorate %1 BuiltIn SampleId\n");
+              R"(OpCapability Shader
+OpCapability SampleRateShading
+)");
+    EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 BuiltIn SampleId
+OpDecorate %1 Flat
+)");
 }
 
 }  // namespace
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index 05f6929..57b26a3 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -115,6 +115,36 @@
     Validate(b);
 }
 
+TEST_F(BuilderTest, GlobalConst_Vec_F16_Constructor) {
+    // const c = vec3<f16>(1h, 2h, 3h);
+    // var v = c;
+    Enable(ast::Extension::kF16);
+
+    auto* c = GlobalConst("c", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 16
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 0x1p+0
+%4 = OpConstant %2 0x1p+1
+%5 = OpConstant %2 0x1.8p+1
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
 TEST_F(BuilderTest, GlobalConst_Vec_AInt_Constructor) {
     // const c = vec3(1, 2, 3);
     // var v = c;
diff --git a/src/tint/writer/spirv/builder_literal_test.cc b/src/tint/writer/spirv/builder_literal_test.cc
index 218db86..374c80b 100644
--- a/src/tint/writer/spirv/builder_literal_test.cc
+++ b/src/tint/writer/spirv/builder_literal_test.cc
@@ -163,4 +163,39 @@
 )");
 }
 
+TEST_F(BuilderTest, Literal_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* i = create<ast::FloatLiteralExpression>(23.245, ast::FloatLiteralExpression::Suffix::kH);
+    WrapInFunction(i);
+
+    spirv::Builder& b = Build();
+
+    auto id = b.GenerateLiteralIfNeeded(nullptr, i);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(2u, id);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1.73cp+4
+)");
+}
+
+TEST_F(BuilderTest, Literal_F16_Dedup) {
+    Enable(ast::Extension::kF16);
+
+    auto* i1 = create<ast::FloatLiteralExpression>(23.245, ast::FloatLiteralExpression::Suffix::kH);
+    auto* i2 = create<ast::FloatLiteralExpression>(23.245, ast::FloatLiteralExpression::Suffix::kH);
+    WrapInFunction(i1, i2);
+
+    spirv::Builder& b = Build();
+
+    ASSERT_NE(b.GenerateLiteralIfNeeded(nullptr, i1), 0u);
+    ASSERT_NE(b.GenerateLiteralIfNeeded(nullptr, i2), 0u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 16
+%2 = OpConstant %1 0x1.73cp+4
+)");
+}
+
 }  // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/builder_type_test.cc b/src/tint/writer/spirv/builder_type_test.cc
index b4bff9b..b3ead65 100644
--- a/src/tint/writer/spirv/builder_type_test.cc
+++ b/src/tint/writer/spirv/builder_type_test.cc
@@ -175,6 +175,34 @@
     ASSERT_FALSE(b.has_error()) << b.error();
 }
 
+TEST_F(BuilderTest_Type, GenerateF16) {
+    auto* f16 = create<sem::F16>();
+
+    spirv::Builder& b = Build();
+
+    auto id = b.GenerateTypeIfNeeded(f16);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(id, 1u);
+
+    ASSERT_EQ(b.types().size(), 1u);
+    EXPECT_EQ(DumpInstruction(b.types()[0]), R"(%1 = OpTypeFloat 16
+)");
+}
+
+TEST_F(BuilderTest_Type, ReturnsGeneratedF16) {
+    auto* f16 = create<sem::F16>();
+    auto* i32 = create<sem::I32>();
+
+    spirv::Builder& b = Build();
+
+    EXPECT_EQ(b.GenerateTypeIfNeeded(f16), 1u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(b.GenerateTypeIfNeeded(i32), 2u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(b.GenerateTypeIfNeeded(f16), 1u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+}
+
 TEST_F(BuilderTest_Type, GenerateI32) {
     auto* i32 = create<sem::I32>();
 
@@ -236,6 +264,39 @@
     ASSERT_FALSE(b.has_error()) << b.error();
 }
 
+TEST_F(BuilderTest_Type, GenerateF16Matrix) {
+    auto* f16 = create<sem::F16>();
+    auto* vec3 = create<sem::Vector>(f16, 3u);
+    auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
+
+    spirv::Builder& b = Build();
+
+    auto id = b.GenerateTypeIfNeeded(mat2x3);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(id, 1u);
+
+    EXPECT_EQ(b.types().size(), 3u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 16
+%2 = OpTypeVector %3 3
+%1 = OpTypeMatrix %2 2
+)");
+}
+
+TEST_F(BuilderTest_Type, ReturnsGeneratedF16Matrix) {
+    auto* f16 = create<sem::F16>();
+    auto* col = create<sem::Vector>(f16, 4u);
+    auto* mat = create<sem::Matrix>(col, 3u);
+
+    spirv::Builder& b = Build();
+
+    EXPECT_EQ(b.GenerateTypeIfNeeded(mat), 1u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(b.GenerateTypeIfNeeded(f16), 3u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+    EXPECT_EQ(b.GenerateTypeIfNeeded(mat), 1u);
+    ASSERT_FALSE(b.has_error()) << b.error();
+}
+
 TEST_F(BuilderTest_Type, GeneratePtr) {
     auto* i32 = create<sem::I32>();
     auto* ptr = create<sem::Pointer>(i32, ast::StorageClass::kOutput, ast::Access::kReadWrite);
diff --git a/src/tint/writer/spirv/scalar_constant.h b/src/tint/writer/spirv/scalar_constant.h
index 14bcefb..0629d0c 100644
--- a/src/tint/writer/spirv/scalar_constant.h
+++ b/src/tint/writer/spirv/scalar_constant.h
@@ -20,6 +20,7 @@
 #include <cstring>
 #include <functional>
 
+#include "src/tint/number.h"
 #include "src/tint/utils/hash.h"
 
 // Forward declarations
@@ -31,6 +32,12 @@
 
 /// ScalarConstant represents a scalar constant value
 struct ScalarConstant {
+    /// The struct type to hold the bits representation of f16 in the Value union
+    struct F16 {
+        /// The 16 bits representation of the f16, stored as uint16_t
+        uint16_t bits_representation;
+    };
+
     /// The constant value
     union Value {
         /// The value as a bool
@@ -41,6 +48,8 @@
         int32_t i32;
         /// The value as a float
         float f32;
+        /// The value as bits representation of a f16
+        F16 f16;
 
         /// The value that is wide enough to encompass all other types (including
         /// future 64-bit data types).
@@ -48,7 +57,7 @@
     };
 
     /// The kind of constant
-    enum class Kind { kBool, kU32, kI32, kF32 };
+    enum class Kind { kBool, kU32, kI32, kF32, kF16 };
 
     /// Constructor
     inline ScalarConstant() { value.u64 = 0; }
@@ -72,7 +81,7 @@
     }
 
     /// @param value the value of the constant
-    /// @returns a new ScalarConstant with the provided value and kind Kind::kI32
+    /// @returns a new ScalarConstant with the provided value and kind Kind::kF32
     static inline ScalarConstant F32(float value) {
         ScalarConstant c;
         c.value.f32 = value;
@@ -81,6 +90,15 @@
     }
 
     /// @param value the value of the constant
+    /// @returns a new ScalarConstant with the provided value and kind Kind::kF16
+    static inline ScalarConstant F16(f16::type value) {
+        ScalarConstant c;
+        c.value.f16 = {f16(value).BitsRepresentation()};
+        c.kind = Kind::kF16;
+        return c;
+    }
+
+    /// @param value the value of the constant
     /// @returns a new ScalarConstant with the provided value and kind Kind::kBool
     static inline ScalarConstant Bool(bool value) {
         ScalarConstant c;
diff --git a/src/tint/writer/spirv/scalar_constant_test.cc b/src/tint/writer/spirv/scalar_constant_test.cc
index 196e600..b00f82a 100644
--- a/src/tint/writer/spirv/scalar_constant_test.cc
+++ b/src/tint/writer/spirv/scalar_constant_test.cc
@@ -52,5 +52,12 @@
     EXPECT_EQ(c.kind, ScalarConstant::Kind::kU32);
 }
 
+TEST_F(SpirvScalarConstantTest, F16) {
+    auto c = ScalarConstant::F16(123.456f);
+    // 123.456f will be quantized to f16 123.4375h, bit pattern 0x57b7
+    EXPECT_EQ(c.value.f16.bits_representation, 0x57b7u);
+    EXPECT_EQ(c.kind, ScalarConstant::Kind::kF16);
+}
+
 }  // namespace
 }  // namespace tint::writer::spirv