Import Tint changes from Dawn

Changes:
  - e9c5070348abd0f33529b225dfcc2cc4703bc3ea tint/writer/msl: Support for F16 type, constructor, and c... by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - 0d3d3210d4ff86ecd3b2e288e091246f8b47af90 tint/writer/glsl: Support for F16 type, constructor, and ... by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - 9c71174f381d35160e67f1c897e28e63c68a0074 tint/resolver: Temporally ban f16 in buffer, pipeline IO ... by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - d31838aff1176b8bfbfdeec2c4f9c7f17360342c tint: Fix x86 build by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: e9c5070348abd0f33529b225dfcc2cc4703bc3ea
Change-Id: Ic4a220ec9a6c0f9ec87bbb9b8c9fa0303a3a0938
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/95882
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 5e5df15..b0495c2 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -334,12 +334,26 @@
     ParamsFor<alias<i32>>(true),    //
     ParamsFor<alias<u32>>(true),    //
     ParamsFor<alias<bool>>(false),  //
+    // Currently entry point IO of f16 types are not implemented yet.
+    // TODO(tint:1473, tint:1502): Change f16 and vecN<f16> cases to valid after f16 is supported in
+    // entry point IO.
+    ParamsFor<f16>(false),          //
+    ParamsFor<vec2<f16>>(false),    //
+    ParamsFor<vec3<f16>>(false),    //
+    ParamsFor<vec4<f16>>(false),    //
+    ParamsFor<mat2x2<f16>>(false),  //
+    ParamsFor<mat3x3<f16>>(false),  //
+    ParamsFor<mat4x4<f16>>(false),  //
+    ParamsFor<alias<f16>>(false),   //
 };
 
 TEST_P(TypeValidationTest, BareInputs) {
     // @fragment
     // fn main(@location(0) @interpolate(flat) a : *) {}
     auto params = GetParam();
+
+    Enable(ast::Extension::kF16);
+
     auto* a = Param("a", params.create_ast_type(*this), {Location(0), Flat()});
     Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
 
@@ -357,6 +371,9 @@
     // @fragment
     // fn main(a : Input) {}
     auto params = GetParam();
+
+    Enable(ast::Extension::kF16);
+
     auto* input =
         Structure("Input", {Member("a", params.create_ast_type(*this), {Location(0), Flat()})});
     auto* a = Param("a", ty.Of(input), {});
@@ -375,6 +392,9 @@
     //   return *();
     // }
     auto params = GetParam();
+
+    Enable(ast::Extension::kF16);
+
     Func(Source{{12, 34}}, "main", {}, params.create_ast_type(*this),
          {Return(Construct(params.create_ast_type(*this)))}, {Stage(ast::PipelineStage::kFragment)},
          {Location(0)});
@@ -395,6 +415,9 @@
     //   return Output();
     // }
     auto params = GetParam();
+
+    Enable(ast::Extension::kF16);
+
     auto* output = Structure("Output", {Member("a", params.create_ast_type(*this), {Location(0)})});
     Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
          {Stage(ast::PipelineStage::kFragment)});
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index d50057c..1bd9ed7 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -107,7 +107,7 @@
     Enable(ast::Extension::kF16);
 
     auto* i1 = Structure("I1", {
-                                   Member(Source{{1, 1}}, "w1", ty.f16()),
+                                   Member(Source{{1, 1}}, "w1", ty.f32()),
                                    Member(Source{{2, 1}}, "x1", ty.f32()),
                                    Member(Source{{3, 1}}, "y1", ty.vec3<f32>()),
                                    Member(Source{{4, 1}}, "z1", ty.array<i32, 4>()),
@@ -115,7 +115,7 @@
     auto* a1 = Alias("a1", ty.Of(i1));
     auto* i2 = Structure("I2", {
                                    Member(Source{{5, 1}}, "x2", ty.mat2x2<f32>()),
-                                   Member(Source{{6, 1}}, "w2", ty.mat3x4<f16>()),
+                                   Member(Source{{6, 1}}, "w2", ty.mat3x4<f32>()),
                                    Member(Source{{7, 1}}, "z2", ty.Of(i1)),
                                });
     auto* a2 = Alias("a2", ty.Of(i2));
diff --git a/src/tint/resolver/pipeline_overridable_constant_test.cc b/src/tint/resolver/pipeline_overridable_constant_test.cc
index 583cc16..3614500 100644
--- a/src/tint/resolver/pipeline_overridable_constant_test.cc
+++ b/src/tint/resolver/pipeline_overridable_constant_test.cc
@@ -103,5 +103,15 @@
     EXPECT_EQ(r()->error(), "12:34 error: pipeline constant IDs must be between 0 and 65535");
 }
 
+TEST_F(ResolverPipelineOverridableConstantTest, F16_TemporallyBan) {
+    Enable(ast::Extension::kF16);
+
+    Override(Source{{12, 34}}, "a", ty.f16(), Expr(1_h), {Id(1u)});
+
+    EXPECT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(r()->error(), "12:34 error: 'override' of type f16 is not implemented yet");
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/storage_class_validation_test.cc
index 3b98fdb..cb3ab16 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/storage_class_validation_test.cc
@@ -23,6 +23,8 @@
 namespace tint::resolver {
 namespace {
 
+using ::testing::HasSubstr;
+
 using ResolverStorageClassValidationTest = ResolverTest;
 
 TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
@@ -99,6 +101,139 @@
 56:78 note: while instantiating 'var' g)");
 }
 
+TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
+    // type a = bool;
+    // var<storage, read> g : a;
+    auto* a = Alias("a", ty.bool_());
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+56:78 note: while instantiating 'var' g)");
+}
+
+// F16 types in storage and uniform buffer is not implemented yet.
+// TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
+TEST_F(ResolverStorageClassValidationTest, StorageBufferF16_TemporallyBan) {
+    // var<storage> g : f16;
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(r()->error(),
+              "56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
+              "implemented yet");
+}
+
+TEST_F(ResolverStorageClassValidationTest, StorageBufferF16Alias_TemporallyBan) {
+    // type a = f16;
+    // var<storage, read> g : a;
+    Enable(ast::Extension::kF16);
+
+    auto* a = Alias("a", ty.f16());
+    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(r()->error(),
+              "56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
+              "implemented yet");
+}
+
+TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF16_TemporallyBan) {
+    // var<storage> g : vec4<f16>;
+    Enable(ast::Extension::kF16);
+    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(r()->error(),
+              "56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
+              "implemented yet");
+}
+
+TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF16_TemporallyBan) {
+    // struct S { a : f16 };
+    // var<storage, read> g : array<S, 3u>;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {Member("a", ty.f16(Source{{56, 78}}))});
+    auto* a = ty.array(ty.Of(s), 3_u);
+    GlobalVar("g", a, ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("56:78 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
+TEST_F(ResolverStorageClassValidationTest, StorageBufferStructF16_TemporallyBan) {
+    // struct S { x : f16 };
+    // var<storage, read> g : S;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
+TEST_F(ResolverStorageClassValidationTest, StorageBufferNoErrorStructF16Aliases_TemporallyBan) {
+    // struct S { x : f16 };
+    // type a1 = S;
+    // var<storage, read> g : a1;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    auto* a1 = Alias("a1", ty.Of(s));
+    auto* a2 = Alias("a2", ty.Of(a1));
+    GlobalVar("g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
 TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
     // var<storage> g : ptr<private, f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
@@ -127,7 +262,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferVector) {
+TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF32) {
     // var<storage> g : vec4<f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
               ast::AttributeList{
@@ -138,7 +273,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
+TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF32) {
     // var<storage, read> g : array<S, 3u>;
     auto* s = Structure("S", {Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
@@ -151,24 +286,6 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
-    // type a = bool;
-    // var<storage, read> g : a;
-    auto* a = Alias("a", ty.bool_());
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
-              ast::AttributeList{
-                  create<ast::BindingAttribute>(0u),
-                  create<ast::GroupAttribute>(0u),
-              });
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(
-        r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
-56:78 note: while instantiating 'var' g)");
-}
-
 TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
     // var<private, read> g : a;
     GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate, ast::Access::kRead);
@@ -207,7 +324,7 @@
               R"(56:78 error: access mode 'write' is not valid for the 'storage' address space)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
+TEST_F(ResolverStorageClassValidationTest, StorageBufferStructI32) {
     // struct S { x : i32 };
     // var<storage, read> g : S;
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
@@ -220,7 +337,7 @@
     ASSERT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
+TEST_F(ResolverStorageClassValidationTest, StorageBufferNoErrorStructI32Aliases) {
     // struct S { x : i32 };
     // type a1 = S;
     // var<storage, read> g : a1;
@@ -271,6 +388,140 @@
 56:78 note: while instantiating 'var' g)");
 }
 
+TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
+    // type a = bool;
+    // var<uniform> g : a;
+    auto* a = Alias("a", ty.bool_());
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
+56:78 note: while instantiating 'var' g)");
+}
+
+// F16 types in storage and uniform buffer is not implemented yet.
+// TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
+TEST_F(ResolverStorageClassValidationTest, UniformBufferF16_TemporallyBan) {
+    // var<uniform> g : f16;
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(r()->error(),
+              "56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
+              "implemented yet");
+}
+
+TEST_F(ResolverStorageClassValidationTest, UniformBufferF16Alias_TemporallyBan) {
+    // type a = f16;
+    // var<uniform> g : a;
+    Enable(ast::Extension::kF16);
+
+    auto* a = Alias("a", ty.f16());
+    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(r()->error(),
+              "56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
+              "implemented yet");
+}
+
+TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF16_TemporallyBan) {
+    // var<uniform> g : vec4<f16>;
+    Enable(ast::Extension::kF16);
+    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("56:78 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
+TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF16_TemporallyBan) {
+    // struct S {
+    //   @size(16) f : f16;
+    // }
+    // var<uniform> g : array<S, 3u>;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {Member("a", ty.f16(Source{{56, 78}}), {MemberSize(16)})});
+    auto* a = ty.array(ty.Of(s), 3_u);
+    GlobalVar("g", a, ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("56:78 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
+TEST_F(ResolverStorageClassValidationTest, UniformBufferStructF16_TemporallyBan) {
+    // struct S { x : f16 };
+    // var<uniform> g :  S;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
+TEST_F(ResolverStorageClassValidationTest, UniformBufferStructF16Aliases_TemporallyBan) {
+    // struct S { x : f16 };
+    // type a1 = S;
+    // var<uniform> g : a1;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    auto* a1 = Alias("a1", ty.Of(s));
+    GlobalVar("g", ty.Of(a1), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
+                          "class is not implemented yet"));
+}
+
 TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
     // var<uniform> g : ptr<private, f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
@@ -299,7 +550,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferVector) {
+TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF32) {
     // var<uniform> g : vec4<f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
               ast::AttributeList{
@@ -310,7 +561,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
+TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF32) {
     // struct S {
     //   @size(16) f : f32;
     // }
@@ -326,25 +577,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
-    // type a = bool;
-    // var<uniform> g : a;
-    auto* a = Alias("a", ty.bool_());
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
-              ast::AttributeList{
-                  create<ast::BindingAttribute>(0u),
-                  create<ast::GroupAttribute>(0u),
-              });
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(
-        r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
-56:78 note: while instantiating 'var' g)");
-}
-
-TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
+TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32) {
     // struct S { x : i32 };
     // var<uniform> g :  S;
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
@@ -357,7 +590,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Aliases) {
+TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32Aliases) {
     // struct S { x : i32 };
     // type a1 = S;
     // var<uniform> g : a1;
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index ee98d03..d99d35b 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -392,6 +392,15 @@
         return true;
     }
 
+    // Temporally forbid using f16 types in "uniform" and "storage" storage class.
+    // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in "uniform" and
+    // "storage" storage class.
+    if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty))) {
+        AddError("using f16 types in 'uniform' or 'storage' storage class is not implemented yet",
+                 source);
+        return false;
+    }
+
     if (auto* str = store_ty->As<sem::Struct>()) {
         for (size_t i = 0; i < str->Members().size(); ++i) {
             auto* const m = str->Members()[i];
@@ -801,6 +810,12 @@
                  decl->source);
         return false;
     }
+
+    if (storage_ty->Is<sem::F16>()) {
+        AddError("'override' of type f16 is not implemented yet", decl->source);
+        return false;
+    }
+
     return true;
 }
 
@@ -1102,6 +1117,13 @@
                                                      const sem::Type* ty, Source source,
                                                      ParamOrRetType param_or_ret,
                                                      bool is_struct_member) {
+        // Temporally forbid using f16 types in entry point IO.
+        // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in entry point IO.
+        if (Is<sem::F16>(sem::Type::DeepestElementOf(ty))) {
+            AddError("entry point IO of f16 types is not implemented yet", source);
+            return false;
+        }
+
         // Scan attributes for pipeline IO attributes.
         // Check for overlap with attributes that have been seen previously.
         const ast::Attribute* pipeline_io_attribute = nullptr;
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index fdf4557..01cbba0 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -159,6 +159,17 @@
     }
 }
 
+bool PrintF16(std::ostream& out, float value) {
+    // Note: Currently inf and nan should not be constructable, and there is no solid way to
+    // generate constant/literal f16 Inf or NaN.
+    if (std::isinf(value) || std::isnan(value)) {
+        return false;
+    } else {
+        out << FloatToString(value) << "hf";
+        return true;
+    }
+}
+
 }  // namespace
 
 SanitizedResult::SanitizedResult() = default;
@@ -313,6 +324,10 @@
         extensions.Append("#extension GL_OES_sample_variables : require");
     }
 
+    if (requires_f16_extension_) {
+        extensions.Append("#extension GL_AMD_gpu_shader_half_float : require");
+    }
+
     auto indent = current_buffer_->current_indent;
 
     if (!extensions.lines.empty()) {
@@ -333,17 +348,12 @@
     return true;
 }
 
-bool GeneratorImpl::RecordExtension(const ast::Enable*) {
-    /*
-    Deal with extension node here, recording it within the generator for
-    later emition.
-    For example:
-    ```
-      if (ext->kind == ast::Enable::ExtensionKind::kF16) {
-      require_fp16_ = true;
-      }
-    ```
-    */
+bool GeneratorImpl::RecordExtension(const ast::Enable* ext) {
+    // Deal with extension node here, recording it within the generator for later emition.
+
+    if (ext->extension == ast::Extension::kF16) {
+        requires_f16_extension_ = true;
+    }
 
     return true;
 }
@@ -2225,6 +2235,7 @@
             PrintF32(out, constant->As<float>());
             return true;
         },
+        [&](const sem::F16*) { return PrintF16(out, constant->As<float>()); },
         [&](const sem::I32*) {
             out << constant->As<AInt>();
             return true;
@@ -2305,6 +2316,9 @@
             return true;
         },
         [&](const ast::FloatLiteralExpression* l) {
+            if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
+                return PrintF16(out, static_cast<float>(l->value));
+            }
             PrintF32(out, static_cast<float>(l->value));
             return true;
         },
@@ -2326,6 +2340,8 @@
         out << "false";
     } else if (type->Is<sem::F32>()) {
         out << "0.0f";
+    } else if (type->Is<sem::F16>()) {
+        out << "0.0hf";
     } else if (type->Is<sem::I32>()) {
         out << "0";
     } else if (type->Is<sem::U32>()) {
@@ -2751,12 +2767,14 @@
     } else if (type->Is<sem::F32>()) {
         out << "float";
     } else if (type->Is<sem::F16>()) {
-        diagnostics_.add_error(diag::System::Writer, "Type f16 is not completely implemented yet.");
-        return false;
+        out << "float16_t";
     } else if (type->Is<sem::I32>()) {
         out << "int";
     } else if (auto* mat = type->As<sem::Matrix>()) {
-        TINT_ASSERT(Writer, mat->type()->Is<sem::F32>());
+        TINT_ASSERT(Writer, (mat->type()->IsAnyOf<sem::F32, sem::F16>()));
+        if (mat->type()->Is<sem::F16>()) {
+            out << "f16";
+        }
         out << "mat" << mat->columns();
         if (mat->rows() != mat->columns()) {
             out << "x" << mat->rows();
@@ -2835,6 +2853,8 @@
         auto width = vec->Width();
         if (vec->type()->Is<sem::F32>() && width >= 1 && width <= 4) {
             out << "vec" << width;
+        } else if (vec->type()->Is<sem::F16>() && width >= 1 && width <= 4) {
+            out << "f16vec" << width;
         } else if (vec->type()->Is<sem::I32>() && width >= 1 && width <= 4) {
             out << "ivec" << width;
         } else if (vec->type()->Is<sem::U32>() && width >= 1 && width <= 4) {
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index 15b7ee1..153e316 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -527,6 +527,7 @@
     std::unordered_set<const sem::Struct*> emitted_structs_;
     bool requires_oes_sample_variables_ = false;
     bool requires_default_precision_qualifier_ = false;
+    bool requires_f16_extension_ = false;
     Version version_;
 };
 
diff --git a/src/tint/writer/glsl/generator_impl_constructor_test.cc b/src/tint/writer/glsl/generator_impl_constructor_test.cc
index 9a937e8..20d6b07 100644
--- a/src/tint/writer/glsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_constructor_test.cc
@@ -61,6 +61,18 @@
     EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
 }
 
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_F16) {
+    Enable(ast::Extension::kF16);
+
+    // Use a number close to 1<<16 but whose decimal representation ends in 0.
+    WrapInFunction(Expr(f16((1 << 15) - 8)));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("32752.0hf"));
+}
+
 TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Float) {
     WrapInFunction(Construct<f32>(-1.2e-5_f));
 
@@ -70,6 +82,17 @@
     EXPECT_THAT(gen.result(), HasSubstr("-0.000012f"));
 }
 
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Construct<f16>(-1.2e-3_h));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("-0.00119972229hf"));
+}
+
 TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Bool) {
     WrapInFunction(Construct<bool>(true));
 
@@ -97,7 +120,7 @@
     EXPECT_THAT(gen.result(), HasSubstr("12345u"));
 }
 
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_F32) {
     WrapInFunction(vec3<f32>(1_f, 2_f, 3_f));
 
     GeneratorImpl& gen = Build();
@@ -106,7 +129,18 @@
     EXPECT_THAT(gen.result(), HasSubstr("vec3(1.0f, 2.0f, 3.0f)"));
 }
 
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(vec3<f16>(1_h, 2_h, 3_h));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("f16vec3(1.0hf, 2.0hf, 3.0hf)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty_F32) {
     WrapInFunction(vec3<f32>());
 
     GeneratorImpl& gen = Build();
@@ -115,7 +149,18 @@
     EXPECT_THAT(gen.result(), HasSubstr("vec3(0.0f)"));
 }
 
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_Float) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(vec3<f16>());
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("f16vec3(0.0hf)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F32_Literal) {
     WrapInFunction(vec3<f32>(2_f));
 
     GeneratorImpl& gen = Build();
@@ -124,6 +169,43 @@
     EXPECT_THAT(gen.result(), HasSubstr("vec3(2.0f)"));
 }
 
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F16_Literal) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(vec3<f16>(2_h));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("f16vec3(2.0hf)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F32_Var) {
+    auto* var = Var("v", nullptr, Expr(2_f));
+    auto* cast = vec3<f32>(var);
+    WrapInFunction(var, cast);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
+  vec3 tint_symbol = vec3(v);)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F16_Var) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("v", nullptr, Expr(2_h));
+    auto* cast = vec3<f16>(var);
+    WrapInFunction(var, cast);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(R"(float16_t v = 2.0hf;
+  f16vec3 tint_symbol = f16vec3(v);)"));
+}
+
 TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_Bool) {
     WrapInFunction(vec3<bool>(true));
 
@@ -151,7 +233,7 @@
     EXPECT_THAT(gen.result(), HasSubstr("uvec3(2u)"));
 }
 
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_F32) {
     WrapInFunction(mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(3_f, 4_f, 5_f)));
 
     GeneratorImpl& gen = Build();
@@ -161,14 +243,135 @@
     EXPECT_THAT(gen.result(), HasSubstr("mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(3.0f, 4.0f, 5.0f))"));
 }
 
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Empty) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(mat2x3<f16>(vec3<f16>(1_h, 2_h, 3_h), vec3<f16>(3_h, 4_h, 5_h)));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(),
+                HasSubstr("f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(3.0hf, 4.0hf, 5.0hf))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Complex_F32) {
+    // mat4x4<f32>(
+    //     vec4<f32>(2.0f, 3.0f, 4.0f, 8.0f),
+    //     vec4<f32>(),
+    //     vec4<f32>(7.0f),
+    //     vec4<f32>(vec4<f32>(42.0f, 21.0f, 6.0f, -5.0f)),
+    //   );
+    auto* vector_literal =
+        vec4<f32>(Expr(f32(2.0)), Expr(f32(3.0)), Expr(f32(4.0)), Expr(f32(8.0)));
+    auto* vector_zero_ctor = vec4<f32>();
+    auto* vector_single_scalar_ctor = vec4<f32>(Expr(f32(7.0)));
+    auto* vector_identical_ctor =
+        vec4<f32>(vec4<f32>(Expr(f32(42.0)), Expr(f32(21.0)), Expr(f32(6.0)), Expr(f32(-5.0))));
+
+    auto* constructor = mat4x4<f32>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+                                    vector_identical_ctor);
+
+    WrapInFunction(constructor);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("mat4(vec4(2.0f, 3.0f, 4.0f, 8.0f), vec4(0.0f), "
+                                        "vec4(7.0f), vec4(42.0f, 21.0f, 6.0f, -5.0f))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Complex_F16) {
+    // mat4x4<f16>(
+    //     vec4<f16>(2.0h, 3.0h, 4.0h, 8.0h),
+    //     vec4<f16>(),
+    //     vec4<f16>(7.0h),
+    //     vec4<f16>(vec4<f16>(42.0h, 21.0h, 6.0h, -5.0h)),
+    //   );
+    Enable(ast::Extension::kF16);
+
+    auto* vector_literal =
+        vec4<f16>(Expr(f16(2.0)), Expr(f16(3.0)), Expr(f16(4.0)), Expr(f16(8.0)));
+    auto* vector_zero_ctor = vec4<f16>();
+    auto* vector_single_scalar_ctor = vec4<f16>(Expr(f16(7.0)));
+    auto* vector_identical_ctor =
+        vec4<f16>(vec4<f16>(Expr(f16(42.0)), Expr(f16(21.0)), Expr(f16(6.0)), Expr(f16(-5.0))));
+
+    auto* constructor = mat4x4<f16>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+                                    vector_identical_ctor);
+
+    WrapInFunction(constructor);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(),
+                HasSubstr("f16mat4(f16vec4(2.0hf, 3.0hf, 4.0hf, 8.0hf), f16vec4(0.0hf), "
+                          "f16vec4(7.0hf), f16vec4(42.0hf, 21.0hf, 6.0hf, -5.0hf))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Empty_F32) {
     WrapInFunction(mat2x3<f32>());
 
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
 
-    EXPECT_THAT(gen.result(), HasSubstr("mat2x3(vec3(0.0f), vec3(0.0f)"));
+    EXPECT_THAT(gen.result(), HasSubstr("mat2x3 tint_symbol = mat2x3(vec3(0.0f), vec3(0.0f))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Empty_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(mat2x3<f16>());
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(),
+                HasSubstr("f16mat2x3 tint_symbol = f16mat2x3(f16vec3(0.0hf), f16vec3(0.0hf))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Identity_F32) {
+    // fn f() {
+    //     var m_1: mat4x4<f32> = mat4x4<f32>();
+    //     var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
+    // }
+
+    auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), mat4x4<f32>());
+    auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), mat4x4<f32>(m_1));
+
+    WrapInFunction(m_1, m_2);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("mat4 m_2 = mat4(m_1);"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Identity_F16) {
+    // fn f() {
+    //     var m_1: mat4x4<f16> = mat4x4<f16>();
+    //     var m_2: mat4x4<f16> = mat4x4<f16>(m_1);
+    // }
+
+    Enable(ast::Extension::kF16);
+
+    auto* m_1 = Var("m_1", ty.mat4x4(ty.f16()), mat4x4<f16>());
+    auto* m_2 = Var("m_2", ty.mat4x4(ty.f16()), mat4x4<f16>(m_1));
+
+    WrapInFunction(m_1, m_2);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("f16mat4 m_2 = f16mat4(m_1);"));
 }
 
 TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Array) {
diff --git a/src/tint/writer/glsl/generator_impl_module_constant_test.cc b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
index b483511..fa269da 100644
--- a/src/tint/writer/glsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
@@ -117,6 +117,26 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalConst("G", nullptr, Expr(1_h));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+  float16_t l = 1.0hf;
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
     auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
     Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
@@ -168,6 +188,26 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalConst("G", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+  f16vec3 l = f16vec3(1.0hf, 2.0hf, 3.0hf);
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
     auto* var = GlobalConst("G", nullptr,
                             Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -203,6 +243,26 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalConst("G", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+  f16mat2x3 l = f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(4.0hf, 5.0hf, 6.0hf));
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
     auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
     Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index acf28ff..1c4cb03 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -105,6 +105,19 @@
     EXPECT_EQ(out.str(), "float");
 }
 
+TEST_F(GlslGeneratorImplTest_Type, EmitType_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* f16 = create<sem::F16>();
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitType(out, f16, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+        << gen.error();
+    EXPECT_EQ(out.str(), "float16_t");
+}
+
 TEST_F(GlslGeneratorImplTest_Type, EmitType_I32) {
     auto* i32 = create<sem::I32>();
 
@@ -116,7 +129,7 @@
     EXPECT_EQ(out.str(), "int");
 }
 
-TEST_F(GlslGeneratorImplTest_Type, EmitType_Matrix) {
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Matrix_F32) {
     auto* f32 = create<sem::F32>();
     auto* vec3 = create<sem::Vector>(f32, 3u);
     auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
@@ -129,6 +142,21 @@
     EXPECT_EQ(out.str(), "mat2x3");
 }
 
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Matrix_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* f16 = create<sem::F16>();
+    auto* vec3 = create<sem::Vector>(f16, 3u);
+    auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+        << gen.error();
+    EXPECT_EQ(out.str(), "f16mat2x3");
+}
+
 TEST_F(GlslGeneratorImplTest_Type, EmitType_StructDecl) {
     auto* s = Structure("S", {
                                  Member("a", ty.i32()),
@@ -213,7 +241,7 @@
     EXPECT_EQ(out.str(), "uint");
 }
 
-TEST_F(GlslGeneratorImplTest_Type, EmitType_Vector) {
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Vector_F32) {
     auto* f32 = create<sem::F32>();
     auto* vec3 = create<sem::Vector>(f32, 3u);
 
@@ -225,6 +253,20 @@
     EXPECT_EQ(out.str(), "vec3");
 }
 
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Vector_F16) {
+    Enable(ast::Extension::kF16);
+
+    auto* f16 = create<sem::F16>();
+    auto* vec3 = create<sem::Vector>(f16, 3u);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitType(out, vec3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+        << gen.error();
+    EXPECT_EQ(out.str(), "f16vec3");
+}
+
 TEST_F(GlslGeneratorImplTest_Type, EmitType_Void) {
     auto* void_ = create<sem::Void>();
 
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index daa664a..873afc1 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -149,6 +149,26 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* C = Const("C", nullptr, Expr(1_h));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+  float16_t l = 1.0hf;
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
     auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
     Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -200,6 +220,26 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* C = Const("C", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+  f16vec3 l = f16vec3(1.0hf, 2.0hf, 3.0hf);
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
     auto* C =
         Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -235,6 +275,26 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* C = Const("C", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+  f16mat2x3 l = f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(4.0hf, 5.0hf, 6.0hf));
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
     auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
     Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -300,7 +360,7 @@
     EXPECT_THAT(gen.result(), HasSubstr("  float a = 0.0f;\n"));
 }
 
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec) {
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec_f32) {
     auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
 
     auto* stmt = Decl(var);
@@ -313,7 +373,22 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat) {
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("a", ty.vec3<f16>(), ast::StorageClass::kNone, vec3<f16>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), R"(f16vec3 a = f16vec3(0.0hf);
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat_f32) {
     auto* var = Var("a", ty.mat2x3<f32>(), ast::StorageClass::kNone, mat2x3<f32>());
 
     auto* stmt = Decl(var);
@@ -327,5 +402,21 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("a", ty.mat2x3<f16>(), ast::StorageClass::kNone, mat2x3<f16>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(),
+              R"(f16mat2x3 a = f16mat2x3(f16vec3(0.0hf), f16vec3(0.0hf));
+)");
+}
+
 }  // namespace
 }  // namespace tint::writer::glsl
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 43324bb..e2b14a6 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -39,6 +39,7 @@
 #include "src/tint/sem/constant.h"
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
+#include "src/tint/sem/f16.h"
 #include "src/tint/sem/f32.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/i32.h"
@@ -97,6 +98,20 @@
     }
 }
 
+void PrintF16(std::ostream& out, float value) {
+    // Note: Currently inf and nan should not be constructable, but this is implemented for the day
+    // we support them.
+    if (std::isinf(value)) {
+        // HUGE_VALH evaluates to +infinity.
+        out << (value >= 0 ? "HUGE_VALH" : "-HUGE_VALH");
+    } else if (std::isnan(value)) {
+        // There is no NaN expr for half in MSL, "NAN" is of float type.
+        out << "NAN";
+    } else {
+        out << FloatToString(value) << "h";
+    }
+}
+
 void PrintI32(std::ostream& out, int32_t value) {
     // MSL (and C++) parse `-2147483648` as a `long` because it parses unary minus and `2147483648`
     // as separate tokens, and the latter doesn't fit into an (32-bit) `int`.
@@ -1551,10 +1566,8 @@
             return true;
         },
         [&](const sem::F16*) {
-            // Placeholder for emitting f16 zero value
-            diagnostics_.add_error(diag::System::Writer,
-                                   "Type f16 is not completely implemented yet");
-            return false;
+            out << "0.0h";
+            return true;
         },
         [&](const sem::F32*) {
             out << "0.0f";
@@ -1605,6 +1618,10 @@
             PrintF32(out, constant->As<float>());
             return true;
         },
+        [&](const sem::F16*) {
+            PrintF16(out, constant->As<float>());
+            return true;
+        },
         [&](const sem::I32*) {
             PrintI32(out, constant->As<int32_t>());
             return true;
@@ -1694,7 +1711,11 @@
             return true;
         },
         [&](const ast::FloatLiteralExpression* l) {
-            PrintF32(out, static_cast<float>(l->value));
+            if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
+                PrintF16(out, static_cast<float>(l->value));
+            } else {
+                PrintF32(out, static_cast<float>(l->value));
+            }
             return true;
         },
         [&](const ast::IntLiteralExpression* i) {
@@ -2459,9 +2480,8 @@
             return true;
         },
         [&](const sem::F16*) {
-            diagnostics_.add_error(diag::System::Writer,
-                                   "Type f16 is not completely implemented yet");
-            return false;
+            out << "half";
+            return true;
         },
         [&](const sem::F32*) {
             out << "float";
@@ -3043,20 +3063,25 @@
         [&](const sem::F32*) {
             return SizeAndAlign{4, 4};
         },
+        [&](const sem::F16*) {
+            return SizeAndAlign{2, 2};
+        },
 
         [&](const sem::Vector* vec) {
             auto num_els = vec->Width();
             auto* el_ty = vec->type();
-            if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
+            SizeAndAlign el_size_align = MslPackedTypeSizeAndAlign(el_ty);
+            if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32, sem::F16>()) {
                 // Use a packed_vec type for 3-element vectors only.
                 if (num_els == 3) {
                     // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
                     // 2.2.3 Packed Vector Types
-                    return SizeAndAlign{num_els * 4, 4};
+                    return SizeAndAlign{num_els * el_size_align.size, el_size_align.align};
                 } else {
                     // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
                     // 2.2 Vector Data Types
-                    return SizeAndAlign{num_els * 4, num_els * 4};
+                    // Vector data types are aligned to their size.
+                    return SizeAndAlign{num_els * el_size_align.size, num_els * el_size_align.size};
                 }
             }
             TINT_UNREACHABLE(Writer, diagnostics_)
@@ -3070,8 +3095,9 @@
             auto cols = mat->columns();
             auto rows = mat->rows();
             auto* el_ty = mat->type();
-            if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
-                static constexpr SizeAndAlign table[] = {
+            // Metal only support half and float matrix.
+            if (el_ty->IsAnyOf<sem::F32, sem::F16>()) {
+                static constexpr SizeAndAlign table_f32[] = {
                     /* float2x2 */ {16, 8},
                     /* float2x3 */ {32, 16},
                     /* float2x4 */ {32, 16},
@@ -3082,8 +3108,23 @@
                     /* float4x3 */ {64, 16},
                     /* float4x4 */ {64, 16},
                 };
+                static constexpr SizeAndAlign table_f16[] = {
+                    /* half2x2 */ {8, 4},
+                    /* half2x3 */ {16, 8},
+                    /* half2x4 */ {16, 8},
+                    /* half3x2 */ {12, 4},
+                    /* half3x3 */ {24, 8},
+                    /* half3x4 */ {24, 8},
+                    /* half4x2 */ {16, 4},
+                    /* half4x3 */ {32, 8},
+                    /* half4x4 */ {32, 8},
+                };
                 if (cols >= 2 && cols <= 4 && rows >= 2 && rows <= 4) {
-                    return table[(3 * (cols - 2)) + (rows - 2)];
+                    if (el_ty->Is<sem::F32>()) {
+                        return table_f32[(3 * (cols - 2)) + (rows - 2)];
+                    } else {
+                        return table_f16[(3 * (cols - 2)) + (rows - 2)];
+                    }
                 }
             }
 
diff --git a/src/tint/writer/msl/generator_impl_constructor_test.cc b/src/tint/writer/msl/generator_impl_constructor_test.cc
index 2fa85f0..14fc4a5 100644
--- a/src/tint/writer/msl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/msl/generator_impl_constructor_test.cc
@@ -61,6 +61,18 @@
     EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
 }
 
+TEST_F(MslGeneratorImplTest, EmitConstructor_F16) {
+    Enable(ast::Extension::kF16);
+
+    // Use a number close to 1<<16 but whose decimal representation ends in 0.
+    WrapInFunction(Expr(f16((1 << 15) - 8)));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("32752.0h"));
+}
+
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Float) {
     WrapInFunction(Construct<f32>(-1.2e-5_f));
 
@@ -70,6 +82,17 @@
     EXPECT_THAT(gen.result(), HasSubstr("-0.000012f"));
 }
 
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Construct<f16>(-1.2e-3_h));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("-0.00119972229h"));
+}
+
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Bool) {
     WrapInFunction(Construct<bool>(true));
 
@@ -97,7 +120,7 @@
     EXPECT_THAT(gen.result(), HasSubstr("12345u"));
 }
 
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec) {
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_F32) {
     WrapInFunction(vec3<f32>(1_f, 2_f, 3_f));
 
     GeneratorImpl& gen = Build();
@@ -106,7 +129,18 @@
     EXPECT_THAT(gen.result(), HasSubstr("float3(1.0f, 2.0f, 3.0f)"));
 }
 
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty) {
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(vec3<f16>(1_h, 2_h, 3_h));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("half3(1.0h, 2.0h, 3.0h)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty_F32) {
     WrapInFunction(vec3<f32>());
 
     GeneratorImpl& gen = Build();
@@ -115,26 +149,230 @@
     EXPECT_THAT(gen.result(), HasSubstr("float3(0.0f)"));
 }
 
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat) {
-    WrapInFunction(Construct(ty.mat2x3<f32>(), vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(3_f, 4_f, 5_f)));
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(vec3<f16>());
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("half3(0.0h)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F32_Literal) {
+    WrapInFunction(vec3<f32>(2_f));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("float3(2.0f)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F16_Literal) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(vec3<f16>(2_h));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("half3(2.0h)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F32_Var) {
+    auto* var = Var("v", nullptr, Expr(2_f));
+    auto* cast = vec3<f32>(var);
+    WrapInFunction(var, cast);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
+  float3 const tint_symbol = float3(v);)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F16_Var) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("v", nullptr, Expr(2_h));
+    auto* cast = vec3<f16>(var);
+    WrapInFunction(var, cast);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(R"(half v = 2.0h;
+  half3 const tint_symbol = half3(v);)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_Bool) {
+    WrapInFunction(vec3<bool>(true));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("bool3(true)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_Int) {
+    WrapInFunction(vec3<i32>(2_i));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("int3(2)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_UInt) {
+    WrapInFunction(vec3<u32>(2_u));
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr("uint3(2u)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_F32) {
+    WrapInFunction(mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(3_f, 4_f, 5_f)));
 
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
 
-    // A matrix of type T with n columns and m rows can also be constructed from
-    // n vectors of type T with m components.
     EXPECT_THAT(gen.result(),
                 HasSubstr("float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))"));
 }
 
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Empty) {
-    WrapInFunction(mat4x4<f32>());
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(mat2x3<f16>(vec3<f16>(1_h, 2_h, 3_h), vec3<f16>(3_h, 4_h, 5_h)));
 
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr("float4x4(float4(0.0f), float4(0.0f)"));
+
+    EXPECT_THAT(gen.result(),
+                HasSubstr("half2x3(half3(1.0h, 2.0h, 3.0h), half3(3.0h, 4.0h, 5.0h))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Complex_F32) {
+    // mat4x4<f32>(
+    //     vec4<f32>(2.0f, 3.0f, 4.0f, 8.0f),
+    //     vec4<f32>(),
+    //     vec4<f32>(7.0f),
+    //     vec4<f32>(vec4<f32>(42.0f, 21.0f, 6.0f, -5.0f)),
+    //   );
+    auto* vector_literal =
+        vec4<f32>(Expr(f32(2.0)), Expr(f32(3.0)), Expr(f32(4.0)), Expr(f32(8.0)));
+    auto* vector_zero_ctor = vec4<f32>();
+    auto* vector_single_scalar_ctor = vec4<f32>(Expr(f32(7.0)));
+    auto* vector_identical_ctor =
+        vec4<f32>(vec4<f32>(Expr(f32(42.0)), Expr(f32(21.0)), Expr(f32(6.0)), Expr(f32(-5.0))));
+
+    auto* constructor = mat4x4<f32>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+                                    vector_identical_ctor);
+
+    WrapInFunction(constructor);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), float4(0.0f), "
+                                        "float4(7.0f), float4(42.0f, 21.0f, 6.0f, -5.0f))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Complex_F16) {
+    // mat4x4<f16>(
+    //     vec4<f16>(2.0h, 3.0h, 4.0h, 8.0h),
+    //     vec4<f16>(),
+    //     vec4<f16>(7.0h),
+    //     vec4<f16>(vec4<f16>(42.0h, 21.0h, 6.0h, -5.0h)),
+    //   );
+    Enable(ast::Extension::kF16);
+
+    auto* vector_literal =
+        vec4<f16>(Expr(f16(2.0)), Expr(f16(3.0)), Expr(f16(4.0)), Expr(f16(8.0)));
+    auto* vector_zero_ctor = vec4<f16>();
+    auto* vector_single_scalar_ctor = vec4<f16>(Expr(f16(7.0)));
+    auto* vector_identical_ctor =
+        vec4<f16>(vec4<f16>(Expr(f16(42.0)), Expr(f16(21.0)), Expr(f16(6.0)), Expr(f16(-5.0))));
+
+    auto* constructor = mat4x4<f16>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+                                    vector_identical_ctor);
+
+    WrapInFunction(constructor);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("half4x4(half4(2.0h, 3.0h, 4.0h, 8.0h), half4(0.0h), "
+                                        "half4(7.0h), half4(42.0h, 21.0h, 6.0h, -5.0h))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Empty_F32) {
+    WrapInFunction(mat2x3<f32>());
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(),
+                HasSubstr("float2x3 const tint_symbol = float2x3(float3(0.0f), float3(0.0f))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Empty_F16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(mat2x3<f16>());
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(),
+                HasSubstr("half2x3 const tint_symbol = half2x3(half3(0.0h), half3(0.0h))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Identity_F32) {
+    // fn f() {
+    //     var m_1: mat4x4<f32> = mat4x4<f32>();
+    //     var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
+    // }
+
+    auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), mat4x4<f32>());
+    auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), mat4x4<f32>(m_1));
+
+    WrapInFunction(m_1, m_2);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Identity_F16) {
+    // fn f() {
+    //     var m_1: mat4x4<f16> = mat4x4<f16>();
+    //     var m_2: mat4x4<f16> = mat4x4<f16>(m_1);
+    // }
+
+    Enable(ast::Extension::kF16);
+
+    auto* m_1 = Var("m_1", ty.mat4x4(ty.f16()), mat4x4<f16>());
+    auto* m_2 = Var("m_2", ty.mat4x4(ty.f16()), mat4x4<f16>(m_1));
+
+    WrapInFunction(m_1, m_2);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("half4x4 m_2 = half4x4(m_1);"));
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Array) {
diff --git a/src/tint/writer/msl/generator_impl_module_constant_test.cc b/src/tint/writer/msl/generator_impl_module_constant_test.cc
index c204686..4834b3d 100644
--- a/src/tint/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/msl/generator_impl_module_constant_test.cc
@@ -112,6 +112,26 @@
 )");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalConst("G", nullptr, Expr(1_h));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  half const l = 1.0h;
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_AInt) {
     auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
     Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
@@ -166,6 +186,26 @@
 )");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalConst("G", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  half3 const l = half3(1.0h, 2.0h, 3.0h);
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_AFloat) {
     auto* var = GlobalConst("G", nullptr,
                             Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -203,6 +243,26 @@
 )");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = GlobalConst("G", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  half2x3 const l = half2x3(half3(1.0h, 2.0h, 3.0h), half3(4.0h, 5.0h, 6.0h));
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_GlobalConst_arr_f32) {
     auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
     Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index 070b1bb..4ed5a76 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -71,6 +71,18 @@
 DECLARE_TYPE(float4x2, 32, 8);
 DECLARE_TYPE(float4x3, 64, 16);
 DECLARE_TYPE(float4x4, 64, 16);
+DECLARE_TYPE(half2, 4, 4);
+DECLARE_TYPE(packed_half3, 6, 2);
+DECLARE_TYPE(half4, 8, 8);
+DECLARE_TYPE(half2x2, 8, 4);
+DECLARE_TYPE(half2x3, 16, 8);
+DECLARE_TYPE(half2x4, 16, 8);
+DECLARE_TYPE(half3x2, 12, 4);
+DECLARE_TYPE(half3x3, 24, 8);
+DECLARE_TYPE(half3x4, 24, 8);
+DECLARE_TYPE(half4x2, 16, 4);
+DECLARE_TYPE(half4x3, 32, 8);
+DECLARE_TYPE(half4x4, 32, 8);
 using uint = unsigned int;
 
 using MslGeneratorImplTest = TestHelper;
@@ -153,6 +165,16 @@
     EXPECT_EQ(out.str(), "float");
 }
 
+TEST_F(MslGeneratorImplTest, EmitType_F16) {
+    auto* f16 = create<sem::F16>();
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitType(out, f16, "")) << gen.error();
+    EXPECT_EQ(out.str(), "half");
+}
+
 TEST_F(MslGeneratorImplTest, EmitType_I32) {
     auto* i32 = create<sem::I32>();
 
@@ -163,7 +185,7 @@
     EXPECT_EQ(out.str(), "int");
 }
 
-TEST_F(MslGeneratorImplTest, EmitType_Matrix) {
+TEST_F(MslGeneratorImplTest, EmitType_Matrix_F32) {
     auto* f32 = create<sem::F32>();
     auto* vec3 = create<sem::Vector>(f32, 3u);
     auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
@@ -175,6 +197,18 @@
     EXPECT_EQ(out.str(), "float2x3");
 }
 
+TEST_F(MslGeneratorImplTest, EmitType_Matrix_F16) {
+    auto* f16 = create<sem::F16>();
+    auto* vec3 = create<sem::Vector>(f16, 3u);
+    auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.error();
+    EXPECT_EQ(out.str(), "half2x3");
+}
+
 TEST_F(MslGeneratorImplTest, EmitType_Pointer) {
     auto* f32 = create<sem::F32>();
     auto* p = create<sem::Pointer>(f32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index 46becb0..10c5b3d 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -154,6 +154,26 @@
 )");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* C = Const("C", nullptr, Expr(1_h));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  half const l = 1.0h;
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
     auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
     Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -208,6 +228,26 @@
 )");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* C = Const("C", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  half3 const l = half3(1.0h, 2.0h, 3.0h);
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
     auto* C =
         Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -245,6 +285,26 @@
 )");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* C = Const("C", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  half2x3 const l = half2x3(half3(1.0h, 2.0h, 3.0h), half3(4.0h, 5.0h, 6.0h));
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_f32) {
     auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
     Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -343,7 +403,7 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Vector) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Vector_f32) {
     auto* var = Var("a", ty.vec2<f32>());
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -356,7 +416,22 @@
     EXPECT_EQ(gen.result(), "  float2 a = 0.0f;\n");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Matrix) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("a", ty.vec2<f16>());
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "  half2 a = 0.0h;\n");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Matrix_f32) {
     auto* var = Var("a", ty.mat3x2<f32>());
 
     auto* stmt = Decl(var);
@@ -370,6 +445,80 @@
     EXPECT_EQ(gen.result(), "  float3x2 a = float3x2(0.0f);\n");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Matrix_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("a", ty.mat3x2<f16>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "  half3x2 a = half3x2(0.0h);\n");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec_f32) {
+    auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), R"(float3 a = float3(0.0f);
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("a", ty.vec3<f16>(), ast::StorageClass::kNone, vec3<f16>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), R"(half3 a = half3(0.0h);
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroMat_f32) {
+    auto* var = Var("a", ty.mat2x3<f32>(), ast::StorageClass::kNone, mat2x3<f32>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(),
+              R"(float2x3 a = float2x3(float3(0.0f), float3(0.0f));
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroMat_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* var = Var("a", ty.mat2x3<f16>(), ast::StorageClass::kNone, mat2x3<f16>());
+
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(),
+              R"(half2x3 a = half2x3(half3(0.0h), half3(0.0h));
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
     GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
@@ -396,19 +545,5 @@
     EXPECT_THAT(gen.result(), HasSubstr("threadgroup float tint_symbol_2;\n"));
 }
 
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec) {
-    auto* zero_vec = vec3<f32>();
-
-    auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, zero_vec);
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(float3 a = float3(0.0f);
-)");
-}
-
 }  // namespace
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/text_generator.cc b/src/tint/writer/text_generator.cc
index 1a1fa5a..3dabb5c 100644
--- a/src/tint/writer/text_generator.cc
+++ b/src/tint/writer/text_generator.cc
@@ -84,12 +84,13 @@
                             << "  lines.size(): " << lines.size();
         return;
     }
-    lines.insert(lines.begin() + static_cast<int64_t>(before), Line{indent, line});
+    using DT = decltype(lines)::difference_type;
+    lines.insert(lines.begin() + static_cast<DT>(before), Line{indent, line});
 }
 
 void TextGenerator::TextBuffer::Append(const TextBuffer& tb) {
     for (auto& line : tb.lines) {
-        // TODO(bclayton): inefficent, consider optimizing
+        // TODO(bclayton): inefficient, consider optimizing
         lines.emplace_back(Line{current_indent + line.indent, line.content});
     }
 }
@@ -104,8 +105,9 @@
     }
     size_t idx = 0;
     for (auto& line : tb.lines) {
-        // TODO(bclayton): inefficent, consider optimizing
-        lines.insert(lines.begin() + static_cast<int64_t>(before + idx),
+        // TODO(bclayton): inefficient, consider optimizing
+        using DT = decltype(lines)::difference_type;
+        lines.insert(lines.begin() + static_cast<DT>(before + idx),
                      Line{indent + line.indent, line.content});
         idx++;
     }