tint: Implement `f16` keyword in Tint frontend

This patch:
1. Add the `f16` WGSL extension.
2. Add `f16` as keyword, and remove it from reserved word list.
3. Add ast::f16 and sem::f16, and implement validation that using `f16`
   type must be with `f16` extension enabled.
4. Add `Number<NumberKindF16>` for f16 literal and constant, and add
   `ast::FloatLiteralExpression::Suffix::kH`.
5. Add placeholder in all writer which report error when try to emit f16
   type.

Bugs: tint:1473, tint:1502
Change-Id: Ifb363beeb2699ed7cac57e07227d1b2cfa8050b4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89922
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com>
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 94efafb..7e66899 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -342,7 +342,7 @@
                 TraverseType(tex->type);
             },
             [&](Default) {
-                if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
+                if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F16, ast::F32,
                                  ast::DepthTexture, ast::DepthMultisampledTexture,
                                  ast::StorageTexture, ast::ExternalTexture, ast::Sampler>()) {
                     UnhandledNode(diagnostics_, ty);
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 962dd02..38a5025 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -172,6 +172,14 @@
         [&](const ast::Bool*) { return builder_->create<sem::Bool>(); },
         [&](const ast::I32*) { return builder_->create<sem::I32>(); },
         [&](const ast::U32*) { return builder_->create<sem::U32>(); },
+        [&](const ast::F16* t) -> sem::F16* {
+            // Validate if f16 type is allowed.
+            if (builder_->AST().Extensions().count(ast::Enable::ExtensionKind::kF16) == 0) {
+                AddError("f16 used without 'f16' extension enabled", t->source);
+                return nullptr;
+            }
+            return builder_->create<sem::F16>();
+        },
         [&](const ast::F32*) { return builder_->create<sem::F32>(); },
         [&](const ast::Vector* t) -> sem::Vector* {
             if (!t->type) {
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index 2a8a1d1..3ec6f94 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -70,6 +70,10 @@
         if (elem_type->Is<sem::U32>()) {
             return sem::Constant(type, sem::Constant::Scalars(result_size, 0_u));
         }
+        // Add f16 zero scalar here
+        if (elem_type->Is<sem::F16>()) {
+            return sem::Constant(type, sem::Constant::Scalars(result_size, f16{0.f}));
+        }
         if (elem_type->Is<sem::F32>()) {
             return sem::Constant(type, sem::Constant::Scalars(result_size, 0_f));
         }
@@ -120,6 +124,11 @@
                     return u32(static_cast<uint32_t>(s));
                 });
             },
+            [&](const sem::F16*) {
+                return value.WithScalarAt(i, [](auto&& s) {  //
+                    return f16{static_cast<float>(s)};
+                });
+            },
             [&](const sem::F32*) {
                 return value.WithScalarAt(i, [](auto&& s) {  //
                     return static_cast<f32>(s);
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 90e18e1..4c2f56d 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -662,6 +662,24 @@
     EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
 }
 
+TEST_F(ResolverTypeValidationTest, F16TypeUsedWithExtension) {
+    // enable f16;
+    // var<private> v : f16;
+    auto* ext = create<ast::Enable>("f16");
+    AST().AddEnable(ext);
+    Global("v", ty.f16(), ast::StorageClass::kPrivate);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, F16TypeUsedWithoutExtension) {
+    // var<private> v : f16;
+    Global("v", ty.f16(), ast::StorageClass::kPrivate);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "error: f16 used without 'f16' extension enabled");
+}
+
 namespace GetCanonicalTests {
 struct Params {
     builder::ast_type_func_ptr create_ast_type;
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 7a66ec4..df9c7c0 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -194,7 +194,7 @@
 
 // https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
 bool Validator::IsHostShareable(const sem::Type* type) const {
-    if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
+    if (type->IsAnyOf<sem::I32, sem::U32, sem::F32, sem::F16>()) {
         return true;
     }
     return Switch(
@@ -1890,11 +1890,13 @@
     using Bool = sem::Bool;
     using I32 = sem::I32;
     using U32 = sem::U32;
+    using F16 = sem::F16;
     using F32 = sem::F32;
 
     const bool is_valid =
         (ty->Is<Bool>() && value_ty->is_scalar()) || (ty->Is<I32>() && value_ty->is_scalar()) ||
-        (ty->Is<U32>() && value_ty->is_scalar()) || (ty->Is<F32>() && value_ty->is_scalar());
+        (ty->Is<U32>() && value_ty->is_scalar()) || (ty->Is<F16>() && value_ty->is_scalar()) ||
+        (ty->Is<F32>() && value_ty->is_scalar());
     if (!is_valid) {
         AddError("cannot construct '" + sem_.TypeNameOf(ty) + "' with a value of type '" +
                      sem_.TypeNameOf(value_ty) + "'",