tint: Add builtin type aliases (vec3f, etc)

Fixed: tint:1772
Change-Id: I4bed36ded91ca5288875ed6ea819ff4bbb432186
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112340
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 27ae656..a51de7b 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -408,6 +408,8 @@
     "resolver/resolver.h",
     "resolver/sem_helper.cc",
     "resolver/sem_helper.h",
+    "resolver/type_alias.cc",
+    "resolver/type_alias.h",
     "resolver/uniformity.cc",
     "resolver/uniformity.h",
     "resolver/validator.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index e846756..4f193b5 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -542,6 +542,7 @@
 tint_generated(ast/interpolate_attribute BENCH TEST)
 tint_generated(ast/texel_format BENCH TEST)
 tint_generated(resolver/init_conv_intrinsic)
+tint_generated(resolver/type_alias BENCH TEST)
 tint_generated(sem/builtin_type)
 tint_generated(sem/parameter_usage)
 
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 4e89eb8..179cf88 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -111,6 +111,22 @@
   sample
 }
 
+// https://www.w3.org/TR/WGSL/#vector-types
+enum type_alias {
+  vec2f
+  vec2h
+  vec2i
+  vec2u
+  vec3f
+  vec3h
+  vec3i
+  vec3u
+  vec4f
+  vec4h
+  vec4i
+  vec4u
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WGSL primitive types                                                       //
 // Types may be decorated with @precedence(N) to prioritize which type        //
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 796412b..29f88a7 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -66,6 +66,7 @@
 #include "src/tint/ast/void.h"
 #include "src/tint/ast/while_statement.h"
 #include "src/tint/ast/workgroup_attribute.h"
+#include "src/tint/resolver/type_alias.h"
 #include "src/tint/scope_stack.h"
 #include "src/tint/sem/builtin.h"
 #include "src/tint/symbol_table.h"
@@ -481,9 +482,14 @@
         graph_.resolved_symbols.Add(from, resolved);
     }
 
-    /// @returns true if `name` is the name of a builtin function
+    /// @returns true if `name` is the name of a builtin function, or builtin type alias
     bool IsBuiltin(Symbol name) const {
-        return sem::ParseBuiltinType(symbols_.NameFor(name)) != sem::BuiltinType::kNone;
+        auto s = symbols_.NameFor(name);
+        if (sem::ParseBuiltinType(s) != sem::BuiltinType::kNone ||
+            ParseTypeAlias(s) != TypeAlias::kUndefined) {
+            return true;
+        }
+        return false;
     }
 
     /// Appends an error to the diagnostics that the given symbol cannot be
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index cf55a1d..addd989 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -51,6 +51,7 @@
 #include "src/tint/ast/vector.h"
 #include "src/tint/ast/while_statement.h"
 #include "src/tint/ast/workgroup_attribute.h"
+#include "src/tint/resolver/type_alias.h"
 #include "src/tint/resolver/uniformity.h"
 #include "src/tint/sem/abstract_float.h"
 #include "src/tint/sem/abstract_int.h"
@@ -329,13 +330,16 @@
                     AddNote("'" + name + "' declared here", func->Declaration()->source);
                     return nullptr;
                 },
-                [&](Default) {
+                [&](Default) -> sem::Type* {
                     if (auto* tn = ty->As<ast::TypeName>()) {
                         if (IsBuiltin(tn->name)) {
                             auto name = builder_->Symbols().NameFor(tn->name);
                             AddError("cannot use builtin '" + name + "' as type", ty->source);
                             return nullptr;
                         }
+                        if (auto* t = BuiltinTypeAlias(tn->name)) {
+                            return t;
+                        }
                     }
                     TINT_UNREACHABLE(Resolver, diagnostics_)
                         << "Unhandled resolved type '"
@@ -2228,11 +2232,13 @@
             },
             [&](Default) -> sem::Call* {
                 auto name = builder_->Symbols().NameFor(ident->symbol);
-                auto builtin_type = sem::ParseBuiltinType(name);
-                if (builtin_type != sem::BuiltinType::kNone) {
+                if (auto* alias = BuiltinTypeAlias(ident->symbol)) {
+                    return ty_init_or_conv(alias);
+                }
+                if (auto builtin_type = sem::ParseBuiltinType(name);
+                    builtin_type != sem::BuiltinType::kNone) {
                     return BuiltinCall(expr, builtin_type, args);
                 }
-
                 TINT_ICE(Resolver, diagnostics_)
                     << expr->source << " unhandled CallExpression target:\n"
                     << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>") << "\n"
@@ -2328,6 +2334,40 @@
     return call;
 }
 
+sem::Type* Resolver::BuiltinTypeAlias(Symbol sym) const {
+    auto name = builder_->Symbols().NameFor(sym);
+    auto& b = *builder_;
+    switch (ParseTypeAlias(name)) {
+        case TypeAlias::kVec2F:
+            return b.create<sem::Vector>(b.create<sem::F32>(), 2u);
+        case TypeAlias::kVec3F:
+            return b.create<sem::Vector>(b.create<sem::F32>(), 3u);
+        case TypeAlias::kVec4F:
+            return b.create<sem::Vector>(b.create<sem::F32>(), 4u);
+        case TypeAlias::kVec2H:
+            return b.create<sem::Vector>(b.create<sem::F16>(), 2u);
+        case TypeAlias::kVec3H:
+            return b.create<sem::Vector>(b.create<sem::F16>(), 3u);
+        case TypeAlias::kVec4H:
+            return b.create<sem::Vector>(b.create<sem::F16>(), 4u);
+        case TypeAlias::kVec2I:
+            return b.create<sem::Vector>(b.create<sem::I32>(), 2u);
+        case TypeAlias::kVec3I:
+            return b.create<sem::Vector>(b.create<sem::I32>(), 3u);
+        case TypeAlias::kVec4I:
+            return b.create<sem::Vector>(b.create<sem::I32>(), 4u);
+        case TypeAlias::kVec2U:
+            return b.create<sem::Vector>(b.create<sem::U32>(), 2u);
+        case TypeAlias::kVec3U:
+            return b.create<sem::Vector>(b.create<sem::U32>(), 3u);
+        case TypeAlias::kVec4U:
+            return b.create<sem::Vector>(b.create<sem::U32>(), 4u);
+        case TypeAlias::kUndefined:
+            break;
+    }
+    return nullptr;
+}
+
 void Resolver::CollectTextureSamplerPairs(const sem::Builtin* builtin,
                                           utils::VectorRef<const sem::Expression*> args) const {
     // Collect a texture/sampler pair for this builtin.
@@ -2550,7 +2590,7 @@
         return nullptr;
     }
 
-    if (resolved->Is<sem::Type>()) {
+    if (resolved->Is<sem::Type>() || BuiltinTypeAlias(symbol)) {
         AddError("missing '(' for type initializer or cast", expr->source.End());
         return nullptr;
     }
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index abc7633..56ae912 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -384,6 +384,7 @@
     /// Set the shadowing information on variable declarations.
     /// @note this method must only be called after all semantic nodes are built.
     void SetShadows();
+
     /// StatementScope() does the following:
     /// * Creates the AST -> SEM mapping.
     /// * Assigns `sem` to #current_statement_
@@ -415,6 +416,9 @@
     /// @returns true if the symbol is the name of a builtin function.
     bool IsBuiltin(Symbol) const;
 
+    /// @returns the builtin type alias for the given symbol
+    sem::Type* BuiltinTypeAlias(Symbol) const;
+
     // ArrayInitializerSig represents a unique array initializer signature.
     // It is a tuple of the array type, number of arguments provided and earliest evaluation stage.
     using ArrayInitializerSig =
diff --git a/src/tint/resolver/type_alias.cc b/src/tint/resolver/type_alias.cc
new file mode 100644
index 0000000..124fc8c
--- /dev/null
+++ b/src/tint/resolver/type_alias.cc
@@ -0,0 +1,102 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/resolver/type_alias.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/resolver/type_alias.h"
+
+namespace tint::resolver {
+
+/// ParseTypeAlias parses a TypeAlias from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or TypeAlias::kUndefined if the string could not be parsed.
+TypeAlias ParseTypeAlias(std::string_view str) {
+    if (str == "vec2f") {
+        return TypeAlias::kVec2F;
+    }
+    if (str == "vec2h") {
+        return TypeAlias::kVec2H;
+    }
+    if (str == "vec2i") {
+        return TypeAlias::kVec2I;
+    }
+    if (str == "vec2u") {
+        return TypeAlias::kVec2U;
+    }
+    if (str == "vec3f") {
+        return TypeAlias::kVec3F;
+    }
+    if (str == "vec3h") {
+        return TypeAlias::kVec3H;
+    }
+    if (str == "vec3i") {
+        return TypeAlias::kVec3I;
+    }
+    if (str == "vec3u") {
+        return TypeAlias::kVec3U;
+    }
+    if (str == "vec4f") {
+        return TypeAlias::kVec4F;
+    }
+    if (str == "vec4h") {
+        return TypeAlias::kVec4H;
+    }
+    if (str == "vec4i") {
+        return TypeAlias::kVec4I;
+    }
+    if (str == "vec4u") {
+        return TypeAlias::kVec4U;
+    }
+    return TypeAlias::kUndefined;
+}
+
+std::ostream& operator<<(std::ostream& out, TypeAlias value) {
+    switch (value) {
+        case TypeAlias::kUndefined:
+            return out << "undefined";
+        case TypeAlias::kVec2F:
+            return out << "vec2f";
+        case TypeAlias::kVec2H:
+            return out << "vec2h";
+        case TypeAlias::kVec2I:
+            return out << "vec2i";
+        case TypeAlias::kVec2U:
+            return out << "vec2u";
+        case TypeAlias::kVec3F:
+            return out << "vec3f";
+        case TypeAlias::kVec3H:
+            return out << "vec3h";
+        case TypeAlias::kVec3I:
+            return out << "vec3i";
+        case TypeAlias::kVec3U:
+            return out << "vec3u";
+        case TypeAlias::kVec4F:
+            return out << "vec4f";
+        case TypeAlias::kVec4H:
+            return out << "vec4h";
+        case TypeAlias::kVec4I:
+            return out << "vec4i";
+        case TypeAlias::kVec4U:
+            return out << "vec4u";
+    }
+    return out << "<unknown>";
+}
+
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/type_alias.cc.tmpl b/src/tint/resolver/type_alias.cc.tmpl
new file mode 100644
index 0000000..3843864
--- /dev/null
+++ b/src/tint/resolver/type_alias.cc.tmpl
@@ -0,0 +1,25 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate type_alias.cc
+
+To update the generated file, run:
+    ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "type_alias") -}}
+
+#include "src/tint/resolver/type_alias.h"
+
+namespace tint::resolver {
+
+{{ Eval "ParseEnum" $enum}}
+
+{{ Eval "EnumOStream" $enum}}
+
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/type_alias.h b/src/tint/resolver/type_alias.h
new file mode 100644
index 0000000..40083a3
--- /dev/null
+++ b/src/tint/resolver/type_alias.h
@@ -0,0 +1,64 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/resolver/type_alias.h.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SRC_TINT_RESOLVER_TYPE_ALIAS_H_
+#define SRC_TINT_RESOLVER_TYPE_ALIAS_H_
+
+#include <ostream>
+
+namespace tint::resolver {
+
+/// An enumerator of builtin type aliases.
+enum class TypeAlias {
+    kUndefined,
+    kVec2F,
+    kVec2H,
+    kVec2I,
+    kVec2U,
+    kVec3F,
+    kVec3H,
+    kVec3I,
+    kVec3U,
+    kVec4F,
+    kVec4H,
+    kVec4I,
+    kVec4U,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the TypeAlias
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, TypeAlias value);
+
+/// ParseTypeAlias parses a TypeAlias from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or TypeAlias::kUndefined if the string could not be parsed.
+TypeAlias ParseTypeAlias(std::string_view str);
+
+constexpr const char* kTypeAliasStrings[] = {
+    "vec2f", "vec2h", "vec2i", "vec2u", "vec3f", "vec3h",
+    "vec3i", "vec3u", "vec4f", "vec4h", "vec4i", "vec4u",
+};
+
+}  // namespace tint::resolver
+
+#endif  // SRC_TINT_RESOLVER_TYPE_ALIAS_H_
diff --git a/src/tint/resolver/type_alias.h.tmpl b/src/tint/resolver/type_alias.h.tmpl
new file mode 100644
index 0000000..0a9b2d4
--- /dev/null
+++ b/src/tint/resolver/type_alias.h.tmpl
@@ -0,0 +1,29 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate type_alias.h
+
+To update the generated file, run:
+    ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "type_alias") -}}
+
+#ifndef SRC_TINT_RESOLVER_TYPE_ALIAS_H_
+#define SRC_TINT_RESOLVER_TYPE_ALIAS_H_
+
+#include <ostream>
+
+namespace tint::resolver {
+
+/// An enumerator of builtin type aliases.
+{{ Eval "DeclareEnum" $enum}}
+
+}  // namespace tint::resolver
+
+#endif  // SRC_TINT_RESOLVER_TYPE_ALIAS_H_
diff --git a/src/tint/resolver/type_alias_bench.cc b/src/tint/resolver/type_alias_bench.cc
new file mode 100644
index 0000000..2510352
--- /dev/null
+++ b/src/tint/resolver/type_alias_bench.cc
@@ -0,0 +1,57 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/resolver/type_alias_bench.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/resolver/type_alias.h"
+
+#include <array>
+
+#include "benchmark/benchmark.h"
+
+namespace tint::resolver {
+namespace {
+
+void TypeAliasParser(::benchmark::State& state) {
+    std::array kStrings{
+        "veccf",  "32",    "vVc2f",    "vec2f",  "vec21",     "qqeJf",     "vecll7f", "veqH2pp",
+        "vh",     "Gebh",  "vec2h",    "vevi2h", "ve8WWh",    "Mxxc2",     "vgg2i",   "V2X",
+        "vec23",  "vec2i", "vec2E",    "TTeP2i", "vxxcdd",    "v44c2u",    "veVVSSu", "22RRu",
+        "vec2u",  "vF2u",  "vecu",     "ROOHVu", "ecyf",      "n77rrlcGf", "vec340",  "vec3f",
+        "oof",    "vezz",  "1ipp3f",   "XXec3h", "ve9IInn5h", "HHreSSaYh", "vec3h",   "kk3",
+        "jgRR",   "veb",   "vjc3i",    "vc3i",   "vcq",       "vec3i",     "Nec3i",   "vcvv",
+        "ve3QQ",  "vrcf",  "vecju",    "NNew23", "vec3u",     "ve3u",      "vrrc3u",  "Gec3u",
+        "veFF4f", "vE",    "verrf",    "vec4f",  "vef",       "veJJD",     "v4",      "e4k",
+        "vech",   "Jech",  "vec4h",    "ec4h",   "_KKttcH",   "vexxh",     "__qcF",   "vc4qq",
+        "33e64i", "vec4i", "6QQott4i", "v6c4i",  "zzc4O6",    "vyyc4u",    "vcZZ",    "ecWq4u",
+        "vec4u",  "vOO4u", "oYe4",     "v4",
+    };
+    for (auto _ : state) {
+        for (auto& str : kStrings) {
+            auto result = ParseTypeAlias(str);
+            benchmark::DoNotOptimize(result);
+        }
+    }
+}
+
+BENCHMARK(TypeAliasParser);
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/type_alias_bench.cc.tmpl b/src/tint/resolver/type_alias_bench.cc.tmpl
new file mode 100644
index 0000000..3a48226
--- /dev/null
+++ b/src/tint/resolver/type_alias_bench.cc.tmpl
@@ -0,0 +1,29 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate type_alias_bench.cc
+
+To update the generated file, run:
+    ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "type_alias") -}}
+
+#include "src/tint/resolver/type_alias.h"
+
+#include <array>
+
+#include "benchmark/benchmark.h"
+
+namespace tint::resolver {
+namespace {
+
+{{ Eval "BenchmarkParseEnum" $enum }}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/type_alias_test.cc b/src/tint/resolver/type_alias_test.cc
new file mode 100644
index 0000000..c52d44c
--- /dev/null
+++ b/src/tint/resolver/type_alias_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/resolver/type_alias_test.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/resolver/type_alias.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "src/tint/utils/string.h"
+
+namespace tint::resolver {
+namespace {
+
+namespace parse_print_tests {
+
+struct Case {
+    const char* string;
+    TypeAlias value;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+    return out << "'" << std::string(c.string) << "'";
+}
+
+static constexpr Case kValidCases[] = {
+    {"vec2f", TypeAlias::kVec2F}, {"vec2h", TypeAlias::kVec2H}, {"vec2i", TypeAlias::kVec2I},
+    {"vec2u", TypeAlias::kVec2U}, {"vec3f", TypeAlias::kVec3F}, {"vec3h", TypeAlias::kVec3H},
+    {"vec3i", TypeAlias::kVec3I}, {"vec3u", TypeAlias::kVec3U}, {"vec4f", TypeAlias::kVec4F},
+    {"vec4h", TypeAlias::kVec4H}, {"vec4i", TypeAlias::kVec4I}, {"vec4u", TypeAlias::kVec4U},
+};
+
+static constexpr Case kInvalidCases[] = {
+    {"veccf", TypeAlias::kUndefined},     {"32", TypeAlias::kUndefined},
+    {"vVc2f", TypeAlias::kUndefined},     {"vec21", TypeAlias::kUndefined},
+    {"qqeJh", TypeAlias::kUndefined},     {"vecll7h", TypeAlias::kUndefined},
+    {"veqH2pp", TypeAlias::kUndefined},   {"vi", TypeAlias::kUndefined},
+    {"Gebi", TypeAlias::kUndefined},      {"vevi2u", TypeAlias::kUndefined},
+    {"ve8WWu", TypeAlias::kUndefined},    {"Mxxc2", TypeAlias::kUndefined},
+    {"vgg3f", TypeAlias::kUndefined},     {"V3X", TypeAlias::kUndefined},
+    {"vec33", TypeAlias::kUndefined},     {"vec3E", TypeAlias::kUndefined},
+    {"TTeP3h", TypeAlias::kUndefined},    {"vxxcdd", TypeAlias::kUndefined},
+    {"v44c3i", TypeAlias::kUndefined},    {"veVVSSi", TypeAlias::kUndefined},
+    {"22RRi", TypeAlias::kUndefined},     {"vF3u", TypeAlias::kUndefined},
+    {"vecu", TypeAlias::kUndefined},      {"ROOHVu", TypeAlias::kUndefined},
+    {"ecyf", TypeAlias::kUndefined},      {"n77rrlcGf", TypeAlias::kUndefined},
+    {"vec440", TypeAlias::kUndefined},    {"ooh", TypeAlias::kUndefined},
+    {"vezz", TypeAlias::kUndefined},      {"1ipp4h", TypeAlias::kUndefined},
+    {"XXec4i", TypeAlias::kUndefined},    {"ve9IInn5i", TypeAlias::kUndefined},
+    {"HHreSSaYi", TypeAlias::kUndefined}, {"kk4", TypeAlias::kUndefined},
+    {"jgRR", TypeAlias::kUndefined},      {"veb", TypeAlias::kUndefined},
+};
+
+using TypeAliasParseTest = testing::TestWithParam<Case>;
+
+TEST_P(TypeAliasParseTest, Parse) {
+    const char* string = GetParam().string;
+    TypeAlias expect = GetParam().value;
+    EXPECT_EQ(expect, ParseTypeAlias(string));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, TypeAliasParseTest, testing::ValuesIn(kValidCases));
+INSTANTIATE_TEST_SUITE_P(InvalidCases, TypeAliasParseTest, testing::ValuesIn(kInvalidCases));
+
+using TypeAliasPrintTest = testing::TestWithParam<Case>;
+
+TEST_P(TypeAliasPrintTest, Print) {
+    TypeAlias value = GetParam().value;
+    const char* expect = GetParam().string;
+    EXPECT_EQ(expect, utils::ToString(value));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, TypeAliasPrintTest, testing::ValuesIn(kValidCases));
+
+}  // namespace parse_print_tests
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/type_alias_test.cc.tmpl b/src/tint/resolver/type_alias_test.cc.tmpl
new file mode 100644
index 0000000..2f37251
--- /dev/null
+++ b/src/tint/resolver/type_alias_test.cc.tmpl
@@ -0,0 +1,31 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate type_alias_test.cc
+
+To update the generated file, run:
+    ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "type_alias") -}}
+
+#include "src/tint/resolver/type_alias.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "src/tint/utils/string.h"
+
+namespace tint::resolver {
+namespace {
+
+{{ Eval "TestParsePrintEnum" $enum}}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index fe1aa05..b389fc1 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -1395,5 +1395,56 @@
                                          ParamsFor<array<2, f32>>(2)));
 }  // namespace VectorTests
 
+namespace BuiltinTypeAliasTests {
+struct Params {
+    const char* alias;
+    builder::ast_type_func_ptr type;
+};
+
+template <typename T>
+constexpr Params Case(const char* alias) {
+    return Params{alias, DataType<T>::AST};
+}
+
+using BuiltinTypeAliasTest = ResolverTestWithParam<Params>;
+TEST_P(BuiltinTypeAliasTest, CheckEquivalent) {
+    // var aliased : vecTN;
+    // var explicit : vecN<T>;
+    // explicit = aliased;
+    auto& params = GetParam();
+
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Var("aliased", ty.type_name(params.alias))),
+                   Decl(Var("explicit", params.type(*this))),  //
+                   Assign("explicit", "aliased"));
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+TEST_P(BuiltinTypeAliasTest, Construct) {
+    // var v : vecN<T> = vecTN();
+    auto& params = GetParam();
+
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Var("v", params.type(*this), Construct(ty.type_name(params.alias)))));
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
+                         BuiltinTypeAliasTest,
+                         testing::Values(Case<vec2<f32>>("vec2f"),
+                                         Case<vec3<f32>>("vec3f"),
+                                         Case<vec4<f32>>("vec4f"),
+                                         Case<vec2<f16>>("vec2h"),
+                                         Case<vec3<f16>>("vec3h"),
+                                         Case<vec4<f16>>("vec4h"),
+                                         Case<vec2<i32>>("vec2i"),
+                                         Case<vec3<i32>>("vec3i"),
+                                         Case<vec4<i32>>("vec4i"),
+                                         Case<vec2<u32>>("vec2u"),
+                                         Case<vec3<u32>>("vec3u"),
+                                         Case<vec4<u32>>("vec4u")));
+
+}  // namespace BuiltinTypeAliasTests
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/uniformity.h b/src/tint/resolver/uniformity.h
index 7980801..1139980 100644
--- a/src/tint/resolver/uniformity.h
+++ b/src/tint/resolver/uniformity.h
@@ -16,10 +16,10 @@
 #define SRC_TINT_RESOLVER_UNIFORMITY_H_
 
 // Forward declarations.
-namespace tint {
-namespace resolver {
+namespace tint::resolver {
 struct DependencyGraph;
-}  // namespace resolver
+}  // namespace tint::resolver
+namespace tint {
 class ProgramBuilder;
 }  // namespace tint
 
diff --git a/test/tint/shadowing/alias/builtin/const.wgsl b/test/tint/shadowing/alias/builtin/const.wgsl
new file mode 100644
index 0000000..e1fb581
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/const.wgsl
@@ -0,0 +1,10 @@
+type a = vec3f;
+
+fn f() {
+  {
+    const vec3f = 1;
+    const b = vec3f;
+  }
+  const c = a();
+  const d = vec3f();
+}
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/builtin/const.wgsl.expected.dxc.hlsl
similarity index 100%
copy from test/tint/shadowing/alias/const.wgsl.expected.dxc.hlsl
copy to test/tint/shadowing/alias/builtin/const.wgsl.expected.dxc.hlsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/builtin/const.wgsl.expected.fxc.hlsl
similarity index 100%
copy from test/tint/shadowing/alias/const.wgsl.expected.fxc.hlsl
copy to test/tint/shadowing/alias/builtin/const.wgsl.expected.fxc.hlsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.glsl b/test/tint/shadowing/alias/builtin/const.wgsl.expected.glsl
similarity index 100%
copy from test/tint/shadowing/alias/const.wgsl.expected.glsl
copy to test/tint/shadowing/alias/builtin/const.wgsl.expected.glsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.msl b/test/tint/shadowing/alias/builtin/const.wgsl.expected.msl
similarity index 100%
copy from test/tint/shadowing/alias/const.wgsl.expected.msl
copy to test/tint/shadowing/alias/builtin/const.wgsl.expected.msl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.spvasm b/test/tint/shadowing/alias/builtin/const.wgsl.expected.spvasm
similarity index 100%
copy from test/tint/shadowing/alias/const.wgsl.expected.spvasm
copy to test/tint/shadowing/alias/builtin/const.wgsl.expected.spvasm
diff --git a/test/tint/shadowing/alias/builtin/const.wgsl.expected.wgsl b/test/tint/shadowing/alias/builtin/const.wgsl.expected.wgsl
new file mode 100644
index 0000000..e1fb581
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/const.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+type a = vec3f;
+
+fn f() {
+  {
+    const vec3f = 1;
+    const b = vec3f;
+  }
+  const c = a();
+  const d = vec3f();
+}
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl b/test/tint/shadowing/alias/builtin/let.wgsl
new file mode 100644
index 0000000..9dc6ab9
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl
@@ -0,0 +1,10 @@
+type a = vec3f;
+
+fn f() {
+  {
+    let vec3f = 1;
+    let b = vec3f;
+  }
+  let c = a();
+  let d = vec3f();
+}
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/builtin/let.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..0e6094b
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl.expected.dxc.hlsl
@@ -0,0 +1,13 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  {
+    const int vec3f = 1;
+    const int b = vec3f;
+  }
+  const float3 c = (0.0f).xxx;
+  const float3 d = (0.0f).xxx;
+}
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/builtin/let.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..0e6094b
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl.expected.fxc.hlsl
@@ -0,0 +1,13 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  {
+    const int vec3f = 1;
+    const int b = vec3f;
+  }
+  const float3 c = (0.0f).xxx;
+  const float3 d = (0.0f).xxx;
+}
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl.expected.glsl b/test/tint/shadowing/alias/builtin/let.wgsl.expected.glsl
new file mode 100644
index 0000000..fc24838
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl.expected.glsl
@@ -0,0 +1,15 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void f() {
+  {
+    int vec3f = 1;
+    int b = vec3f;
+  }
+  vec3 c = vec3(0.0f);
+  vec3 d = vec3(0.0f);
+}
+
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl.expected.msl b/test/tint/shadowing/alias/builtin/let.wgsl.expected.msl
new file mode 100644
index 0000000..34d9028
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  {
+    int const vec3f = 1;
+    int const b = vec3f;
+  }
+  float3 const c = float3(0.0f);
+  float3 const d = float3(0.0f);
+}
+
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl.expected.spvasm b/test/tint/shadowing/alias/builtin/let.wgsl.expected.spvasm
new file mode 100644
index 0000000..af04d0b
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl.expected.spvasm
@@ -0,0 +1,26 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 12
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+         %11 = OpConstantNull %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %1
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/shadowing/alias/builtin/let.wgsl.expected.wgsl b/test/tint/shadowing/alias/builtin/let.wgsl.expected.wgsl
new file mode 100644
index 0000000..9dc6ab9
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/let.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+type a = vec3f;
+
+fn f() {
+  {
+    let vec3f = 1;
+    let b = vec3f;
+  }
+  let c = a();
+  let d = vec3f();
+}
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl b/test/tint/shadowing/alias/builtin/param.wgsl
new file mode 100644
index 0000000..fd91034
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl
@@ -0,0 +1,3 @@
+fn f(vec3f : vec3f) {
+  let b = vec3f;
+}
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/builtin/param.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..9cd33b7
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl.expected.dxc.hlsl
@@ -0,0 +1,8 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f(float3 vec3f) {
+  const float3 b = vec3f;
+}
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/builtin/param.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..9cd33b7
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl.expected.fxc.hlsl
@@ -0,0 +1,8 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f(float3 vec3f) {
+  const float3 b = vec3f;
+}
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl.expected.glsl b/test/tint/shadowing/alias/builtin/param.wgsl.expected.glsl
new file mode 100644
index 0000000..1d19750
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl.expected.glsl
@@ -0,0 +1,10 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void f(vec3 vec3f) {
+  vec3 b = vec3f;
+}
+
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl.expected.msl b/test/tint/shadowing/alias/builtin/param.wgsl.expected.msl
new file mode 100644
index 0000000..252bf49
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl.expected.msl
@@ -0,0 +1,7 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f(float3 vec3f) {
+  float3 const b = vec3f;
+}
+
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl.expected.spvasm b/test/tint/shadowing/alias/builtin/param.wgsl.expected.spvasm
new file mode 100644
index 0000000..bb64ce1
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl.expected.spvasm
@@ -0,0 +1,26 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 11
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %vec3f "vec3f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %5 = OpTypeFunction %void %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %5
+      %vec3f = OpFunctionParameter %v3float
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/shadowing/alias/builtin/param.wgsl.expected.wgsl b/test/tint/shadowing/alias/builtin/param.wgsl.expected.wgsl
new file mode 100644
index 0000000..fd91034
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/param.wgsl.expected.wgsl
@@ -0,0 +1,3 @@
+fn f(vec3f : vec3f) {
+  let b = vec3f;
+}
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl b/test/tint/shadowing/alias/builtin/var.wgsl
new file mode 100644
index 0000000..e33e30b
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl
@@ -0,0 +1,10 @@
+type a = vec3f;
+
+fn f() {
+  {
+    var vec3f = 1;
+    var b = vec3f;
+  }
+  var c = a();
+  var d = vec3f();
+}
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/builtin/var.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..8a019ba
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl.expected.dxc.hlsl
@@ -0,0 +1,13 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  {
+    int vec3f = 1;
+    int b = vec3f;
+  }
+  float3 c = (0.0f).xxx;
+  float3 d = (0.0f).xxx;
+}
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/builtin/var.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..8a019ba
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl.expected.fxc.hlsl
@@ -0,0 +1,13 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void f() {
+  {
+    int vec3f = 1;
+    int b = vec3f;
+  }
+  float3 c = (0.0f).xxx;
+  float3 d = (0.0f).xxx;
+}
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl.expected.glsl b/test/tint/shadowing/alias/builtin/var.wgsl.expected.glsl
new file mode 100644
index 0000000..fc24838
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl.expected.glsl
@@ -0,0 +1,15 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void f() {
+  {
+    int vec3f = 1;
+    int b = vec3f;
+  }
+  vec3 c = vec3(0.0f);
+  vec3 d = vec3(0.0f);
+}
+
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl.expected.msl b/test/tint/shadowing/alias/builtin/var.wgsl.expected.msl
new file mode 100644
index 0000000..1446f18
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  {
+    int vec3f = 1;
+    int b = vec3f;
+  }
+  float3 c = float3(0.0f);
+  float3 d = float3(0.0f);
+}
+
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl.expected.spvasm b/test/tint/shadowing/alias/builtin/var.wgsl.expected.spvasm
new file mode 100644
index 0000000..52051a9
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl.expected.spvasm
@@ -0,0 +1,42 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 20
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %vec3f "vec3f"
+               OpName %b "b"
+               OpName %c "c"
+               OpName %d "d"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %11 = OpConstantNull %int
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+         %16 = OpConstantNull %v3float
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %void None %1
+          %6 = OpLabel
+      %vec3f = OpVariable %_ptr_Function_int Function %11
+          %b = OpVariable %_ptr_Function_int Function %11
+          %c = OpVariable %_ptr_Function_v3float Function %16
+          %d = OpVariable %_ptr_Function_v3float Function %16
+               OpStore %vec3f %int_1
+         %12 = OpLoad %int %vec3f
+               OpStore %b %12
+               OpStore %c %16
+               OpStore %d %16
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/shadowing/alias/builtin/var.wgsl.expected.wgsl b/test/tint/shadowing/alias/builtin/var.wgsl.expected.wgsl
new file mode 100644
index 0000000..e33e30b
--- /dev/null
+++ b/test/tint/shadowing/alias/builtin/var.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+type a = vec3f;
+
+fn f() {
+  {
+    var vec3f = 1;
+    var b = vec3f;
+  }
+  var c = a();
+  var d = vec3f();
+}
diff --git a/test/tint/shadowing/alias/const.wgsl b/test/tint/shadowing/alias/user/const.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl
rename to test/tint/shadowing/alias/user/const.wgsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/user/const.wgsl.expected.dxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl.expected.dxc.hlsl
rename to test/tint/shadowing/alias/user/const.wgsl.expected.dxc.hlsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/user/const.wgsl.expected.fxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl.expected.fxc.hlsl
rename to test/tint/shadowing/alias/user/const.wgsl.expected.fxc.hlsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.glsl b/test/tint/shadowing/alias/user/const.wgsl.expected.glsl
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl.expected.glsl
rename to test/tint/shadowing/alias/user/const.wgsl.expected.glsl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.msl b/test/tint/shadowing/alias/user/const.wgsl.expected.msl
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl.expected.msl
rename to test/tint/shadowing/alias/user/const.wgsl.expected.msl
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.spvasm b/test/tint/shadowing/alias/user/const.wgsl.expected.spvasm
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl.expected.spvasm
rename to test/tint/shadowing/alias/user/const.wgsl.expected.spvasm
diff --git a/test/tint/shadowing/alias/const.wgsl.expected.wgsl b/test/tint/shadowing/alias/user/const.wgsl.expected.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/const.wgsl.expected.wgsl
rename to test/tint/shadowing/alias/user/const.wgsl.expected.wgsl
diff --git a/test/tint/shadowing/alias/let.wgsl b/test/tint/shadowing/alias/user/let.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl
rename to test/tint/shadowing/alias/user/let.wgsl
diff --git a/test/tint/shadowing/alias/let.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/user/let.wgsl.expected.dxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl.expected.dxc.hlsl
rename to test/tint/shadowing/alias/user/let.wgsl.expected.dxc.hlsl
diff --git a/test/tint/shadowing/alias/let.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/user/let.wgsl.expected.fxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl.expected.fxc.hlsl
rename to test/tint/shadowing/alias/user/let.wgsl.expected.fxc.hlsl
diff --git a/test/tint/shadowing/alias/let.wgsl.expected.glsl b/test/tint/shadowing/alias/user/let.wgsl.expected.glsl
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl.expected.glsl
rename to test/tint/shadowing/alias/user/let.wgsl.expected.glsl
diff --git a/test/tint/shadowing/alias/let.wgsl.expected.msl b/test/tint/shadowing/alias/user/let.wgsl.expected.msl
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl.expected.msl
rename to test/tint/shadowing/alias/user/let.wgsl.expected.msl
diff --git a/test/tint/shadowing/alias/let.wgsl.expected.spvasm b/test/tint/shadowing/alias/user/let.wgsl.expected.spvasm
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl.expected.spvasm
rename to test/tint/shadowing/alias/user/let.wgsl.expected.spvasm
diff --git a/test/tint/shadowing/alias/let.wgsl.expected.wgsl b/test/tint/shadowing/alias/user/let.wgsl.expected.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/let.wgsl.expected.wgsl
rename to test/tint/shadowing/alias/user/let.wgsl.expected.wgsl
diff --git a/test/tint/shadowing/alias/param.wgsl b/test/tint/shadowing/alias/user/param.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl
rename to test/tint/shadowing/alias/user/param.wgsl
diff --git a/test/tint/shadowing/alias/param.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/user/param.wgsl.expected.dxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl.expected.dxc.hlsl
rename to test/tint/shadowing/alias/user/param.wgsl.expected.dxc.hlsl
diff --git a/test/tint/shadowing/alias/param.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/user/param.wgsl.expected.fxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl.expected.fxc.hlsl
rename to test/tint/shadowing/alias/user/param.wgsl.expected.fxc.hlsl
diff --git a/test/tint/shadowing/alias/param.wgsl.expected.glsl b/test/tint/shadowing/alias/user/param.wgsl.expected.glsl
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl.expected.glsl
rename to test/tint/shadowing/alias/user/param.wgsl.expected.glsl
diff --git a/test/tint/shadowing/alias/param.wgsl.expected.msl b/test/tint/shadowing/alias/user/param.wgsl.expected.msl
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl.expected.msl
rename to test/tint/shadowing/alias/user/param.wgsl.expected.msl
diff --git a/test/tint/shadowing/alias/param.wgsl.expected.spvasm b/test/tint/shadowing/alias/user/param.wgsl.expected.spvasm
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl.expected.spvasm
rename to test/tint/shadowing/alias/user/param.wgsl.expected.spvasm
diff --git a/test/tint/shadowing/alias/param.wgsl.expected.wgsl b/test/tint/shadowing/alias/user/param.wgsl.expected.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/param.wgsl.expected.wgsl
rename to test/tint/shadowing/alias/user/param.wgsl.expected.wgsl
diff --git a/test/tint/shadowing/alias/var.wgsl b/test/tint/shadowing/alias/user/var.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl
rename to test/tint/shadowing/alias/user/var.wgsl
diff --git a/test/tint/shadowing/alias/var.wgsl.expected.dxc.hlsl b/test/tint/shadowing/alias/user/var.wgsl.expected.dxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl.expected.dxc.hlsl
rename to test/tint/shadowing/alias/user/var.wgsl.expected.dxc.hlsl
diff --git a/test/tint/shadowing/alias/var.wgsl.expected.fxc.hlsl b/test/tint/shadowing/alias/user/var.wgsl.expected.fxc.hlsl
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl.expected.fxc.hlsl
rename to test/tint/shadowing/alias/user/var.wgsl.expected.fxc.hlsl
diff --git a/test/tint/shadowing/alias/var.wgsl.expected.glsl b/test/tint/shadowing/alias/user/var.wgsl.expected.glsl
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl.expected.glsl
rename to test/tint/shadowing/alias/user/var.wgsl.expected.glsl
diff --git a/test/tint/shadowing/alias/var.wgsl.expected.msl b/test/tint/shadowing/alias/user/var.wgsl.expected.msl
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl.expected.msl
rename to test/tint/shadowing/alias/user/var.wgsl.expected.msl
diff --git a/test/tint/shadowing/alias/var.wgsl.expected.spvasm b/test/tint/shadowing/alias/user/var.wgsl.expected.spvasm
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl.expected.spvasm
rename to test/tint/shadowing/alias/user/var.wgsl.expected.spvasm
diff --git a/test/tint/shadowing/alias/var.wgsl.expected.wgsl b/test/tint/shadowing/alias/user/var.wgsl.expected.wgsl
similarity index 100%
rename from test/tint/shadowing/alias/var.wgsl.expected.wgsl
rename to test/tint/shadowing/alias/user/var.wgsl.expected.wgsl
diff --git a/tools/src/tint/intrinsic/lexer/lexer.go b/tools/src/tint/intrinsic/lexer/lexer.go
index dcec9fe..51045d6 100644
--- a/tools/src/tint/intrinsic/lexer/lexer.go
+++ b/tools/src/tint/intrinsic/lexer/lexer.go
@@ -93,15 +93,26 @@
 			case l.match("/", tok.Divide):
 			case l.match(".", tok.Dot):
 			case l.match("->", tok.Arrow):
-			case l.match("fn", tok.Function):
-			case l.match("op", tok.Operator):
-			case l.match("enum", tok.Enum):
-			case l.match("type", tok.Type):
-			case l.match("init", tok.Initializer):
-			case l.match("conv", tok.Converter):
-			case l.match("match", tok.Match):
 			case unicode.IsLetter(l.peek(0)) || l.peek(0) == '_':
-				l.tok(l.count(alphaNumericOrUnderscore), tok.Identifier)
+				n := l.count(alphaNumericOrUnderscore)
+				switch string(l.runes[:n]) {
+				case "fn":
+					l.tok(n, tok.Function)
+				case "op":
+					l.tok(n, tok.Operator)
+				case "enum":
+					l.tok(n, tok.Enum)
+				case "type":
+					l.tok(n, tok.Type)
+				case "init":
+					l.tok(n, tok.Initializer)
+				case "conv":
+					l.tok(n, tok.Converter)
+				case "match":
+					l.tok(n, tok.Match)
+				default:
+					l.tok(n, tok.Identifier)
+				}
 			case unicode.IsNumber(l.peek(0)) || l.peek(0) == '-':
 				isFloat := false
 				isNegative := false
diff --git a/tools/src/tint/intrinsic/lexer/lexer_test.go b/tools/src/tint/intrinsic/lexer/lexer_test.go
index e74fcd4..9eda8b6 100644
--- a/tools/src/tint/intrinsic/lexer/lexer_test.go
+++ b/tools/src/tint/intrinsic/lexer/lexer_test.go
@@ -84,6 +84,9 @@
 		{"op", []tok.Token{{Kind: tok.Operator, Runes: []rune("op"), Source: tok.Source{
 			S: loc(1, 1, 0), E: loc(1, 3, 2),
 		}}}},
+		{"operation", []tok.Token{{Kind: tok.Identifier, Runes: []rune("operation"), Source: tok.Source{
+			S: loc(1, 1, 0), E: loc(1, 10, 9),
+		}}}},
 		{"type", []tok.Token{{Kind: tok.Type, Runes: []rune("type"), Source: tok.Source{
 			S: loc(1, 1, 0), E: loc(1, 5, 4),
 		}}}},