tint/resolver: Temporally ban f16 in buffer, pipeline IO and override

This patch make resolver reject using f16 types in uniform or storage
buffer, pipeline IO or overridable variable, since these are not
implemented yet. This can help prevent hitting invalid path in writers.

Bug: tint:1473, tint:1502
Change-Id: I5ea753e4254276a6d141d7012a6d0987423a61cf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95827
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: Zhaoming Jiang <zhaoming.jiang@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.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;