tint/ast: Change Function::symbol to Function::name

Function::name is an ast::Identifier.

The goal here is to have all AST nodes use an identifier instead of
symbols directly. This will greatly simplify the renamer transform,
and gives the symbol a Source location, which is helpful for
diagnostics and tooling.

Change-Id: I723a9a104668758db2cb32051efa1f6d3c105913
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119280
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/ast/function.cc b/src/tint/ast/function.cc
index 0f1d7e7..5c0597a 100644
--- a/src/tint/ast/function.cc
+++ b/src/tint/ast/function.cc
@@ -25,27 +25,30 @@
 Function::Function(ProgramID pid,
                    NodeID nid,
                    const Source& src,
-                   Symbol sym,
+                   const Identifier* n,
                    utils::VectorRef<const Parameter*> parameters,
                    const Type* return_ty,
                    const BlockStatement* b,
                    utils::VectorRef<const Attribute*> attrs,
                    utils::VectorRef<const Attribute*> return_type_attrs)
     : Base(pid, nid, src),
-      symbol(sym),
+      name(n),
       params(std::move(parameters)),
       return_type(return_ty),
       body(b),
       attributes(std::move(attrs)),
       return_type_attributes(std::move(return_type_attrs)) {
-    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, symbol, program_id);
+    TINT_ASSERT(AST, name);
+    if (name) {
+        TINT_ASSERT(AST, !name->Is<TemplatedIdentifier>());
+    }
+    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, name, program_id);
     TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, return_ty, program_id);
     TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
     for (auto* param : params) {
         TINT_ASSERT(AST, tint::Is<Parameter>(param));
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, param, program_id);
     }
-    TINT_ASSERT(AST, symbol.IsValid());
     for (auto* attr : attributes) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
     }
@@ -68,18 +71,18 @@
 const Function* Function::Clone(CloneContext* ctx) const {
     // Clone arguments outside of create() call to have deterministic ordering
     auto src = ctx->Clone(source);
-    auto sym = ctx->Clone(symbol);
+    auto n = ctx->Clone(name);
     auto p = ctx->Clone(params);
     auto* ret = ctx->Clone(return_type);
     auto* b = ctx->Clone(body);
     auto attrs = ctx->Clone(attributes);
     auto ret_attrs = ctx->Clone(return_type_attributes);
-    return ctx->dst->create<Function>(src, sym, p, ret, b, attrs, ret_attrs);
+    return ctx->dst->create<Function>(src, n, p, ret, b, attrs, ret_attrs);
 }
 
 const Function* FunctionList::Find(Symbol sym) const {
     for (auto* func : *this) {
-        if (func->symbol == sym) {
+        if (func->name->symbol == sym) {
             return func;
         }
     }
@@ -88,7 +91,7 @@
 
 const Function* FunctionList::Find(Symbol sym, PipelineStage stage) const {
     for (auto* func : *this) {
-        if (func->symbol == sym && func->PipelineStage() == stage) {
+        if (func->name->symbol == sym && func->PipelineStage() == stage) {
             return func;
         }
     }
diff --git a/src/tint/ast/function.h b/src/tint/ast/function.h
index 61771bb..fd39ba3 100644
--- a/src/tint/ast/function.h
+++ b/src/tint/ast/function.h
@@ -29,6 +29,11 @@
 #include "src/tint/ast/parameter.h"
 #include "src/tint/ast/pipeline_stage.h"
 
+// Forward declarations
+namespace tint::ast {
+class Identifier;
+}  // namespace tint::ast
+
 namespace tint::ast {
 
 /// A Function statement.
@@ -38,7 +43,7 @@
     /// @param pid the identifier of the program that owns this node
     /// @param nid the unique node identifier
     /// @param source the variable source
-    /// @param symbol the function symbol
+    /// @param name the function name
     /// @param params the function parameters
     /// @param return_type the return type
     /// @param body the function body
@@ -47,7 +52,7 @@
     Function(ProgramID pid,
              NodeID nid,
              const Source& source,
-             Symbol symbol,
+             const Identifier* name,
              utils::VectorRef<const Parameter*> params,
              const Type* return_type,
              const BlockStatement* body,
@@ -70,8 +75,8 @@
     /// @return the newly cloned node
     const Function* Clone(CloneContext* ctx) const override;
 
-    /// The function symbol
-    const Symbol symbol;
+    /// The function name
+    const Identifier* const name;
 
     /// The function params
     const utils::Vector<const Parameter*, 8> params;
diff --git a/src/tint/ast/function_test.cc b/src/tint/ast/function_test.cc
index 1807bf3..90afda1 100644
--- a/src/tint/ast/function_test.cc
+++ b/src/tint/ast/function_test.cc
@@ -31,7 +31,7 @@
     auto* var = params[0];
 
     auto* f = Func("func", params, i32, utils::Empty);
-    EXPECT_EQ(f->symbol, Symbols().Get("func"));
+    EXPECT_EQ(f->name->symbol, Symbols().Get("func"));
     ASSERT_EQ(f->params.Length(), 1u);
     EXPECT_EQ(f->return_type, i32);
     EXPECT_EQ(f->params[0], var);
@@ -42,7 +42,7 @@
     auto* var = params[0];
 
     auto* f = Func("func", params, ty.void_(), utils::Empty);
-    EXPECT_EQ(f->symbol, Symbols().Get("func"));
+    EXPECT_EQ(f->name->symbol, Symbols().Get("func"));
     ASSERT_EQ(f->params.Length(), 1u);
     EXPECT_EQ(f->return_type, nullptr);
     EXPECT_EQ(f->params[0], var);
@@ -53,7 +53,7 @@
     auto* var = params[0];
 
     auto* f = Func("func", params, ty.void_(), utils::Empty);
-    EXPECT_EQ(f->symbol, Symbols().Get("func"));
+    EXPECT_EQ(f->name->symbol, Symbols().Get("func"));
     ASSERT_EQ(f->params.Length(), 1u);
     EXPECT_EQ(f->return_type, nullptr);
     EXPECT_EQ(f->params[0], var);
@@ -98,6 +98,24 @@
     EXPECT_EQ(src.range.begin.column, 2u);
 }
 
+TEST_F(FunctionTest, Assert_NullName) {
+    EXPECT_FATAL_FAILURE(
+        {
+            ProgramBuilder b;
+            b.Func(static_cast<Identifier*>(nullptr), utils::Empty, b.ty.void_(), utils::Empty);
+        },
+        "internal compiler error");
+}
+
+TEST_F(FunctionTest, Assert_TemplatedName) {
+    EXPECT_FATAL_FAILURE(
+        {
+            ProgramBuilder b;
+            b.Func(b.Ident("a", "b"), utils::Empty, b.ty.void_(), utils::Empty);
+        },
+        "internal compiler error");
+}
+
 TEST_F(FunctionTest, Assert_InvalidName) {
     EXPECT_FATAL_FAILURE(
         {
@@ -107,7 +125,7 @@
         "internal compiler error");
 }
 
-TEST_F(FunctionTest, Assert_Null_Param) {
+TEST_F(FunctionTest, Assert_NullParam) {
     using ParamList = utils::Vector<const ast::Parameter*, 2>;
     EXPECT_FATAL_FAILURE(
         {
diff --git a/src/tint/ast/module_test.cc b/src/tint/ast/module_test.cc
index b032561..1a57fbb 100644
--- a/src/tint/ast/module_test.cc
+++ b/src/tint/ast/module_test.cc
@@ -61,9 +61,9 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.AST().AddFunction(b2.create<ast::Function>(b2.Symbols().Register("func"),
-                                                          utils::Empty, b2.ty.f32(), b2.Block(),
-                                                          utils::Empty, utils::Empty));
+            b1.AST().AddFunction(b2.create<ast::Function>(b2.Ident("func"), utils::Empty,
+                                                          b2.ty.f32(), b2.Block(), utils::Empty,
+                                                          utils::Empty));
         },
         "internal compiler error");
 }
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index b07536d..ca3f33a 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -22,6 +22,7 @@
 #include "src/tint/ast/extension.h"
 #include "src/tint/ast/float_literal_expression.h"
 #include "src/tint/ast/id_attribute.h"
+#include "src/tint/ast/identifier.h"
 #include "src/tint/ast/int_literal_expression.h"
 #include "src/tint/ast/interpolate_attribute.h"
 #include "src/tint/ast/location_attribute.h"
@@ -180,8 +181,8 @@
 
     auto* sem = program_->Sem().Get(func);
 
-    entry_point.name = program_->Symbols().NameFor(func->symbol);
-    entry_point.remapped_name = program_->Symbols().NameFor(func->symbol);
+    entry_point.name = program_->Symbols().NameFor(func->name->symbol);
+    entry_point.remapped_name = program_->Symbols().NameFor(func->name->symbol);
 
     switch (func->PipelineStage()) {
         case ast::PipelineStage::kCompute: {
@@ -877,7 +878,7 @@
 
                                     for (auto* entry_point : entry_points) {
                                         const auto& ep_name = program_->Symbols().NameFor(
-                                            entry_point->Declaration()->symbol);
+                                            entry_point->Declaration()->name->symbol);
                                         (*sampler_targets_)[ep_name].Add(
                                             {sampler_binding_point, texture_binding_point});
                                     }
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index cde71e2..cbc7165 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -27,6 +27,7 @@
 #include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/function.h"
 #include "src/tint/ast/id_attribute.h"
+#include "src/tint/ast/identifier.h"
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ast/int_literal_expression.h"
 #include "src/tint/ast/literal_expression.h"
@@ -178,7 +179,7 @@
     TINT_ASSERT(IR, flow_stack.IsEmpty());
 
     auto* ir_func = builder.CreateFunction();
-    ir_func->name = CloneSymbol(ast_func->symbol);
+    ir_func->name = CloneSymbol(ast_func->name->symbol);
     current_function_ = ir_func;
     builder.ir.functions.Push(ir_func);
 
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 5455609..723a618 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -2510,7 +2510,7 @@
             block = Block(std::forward<BODY>(body));
         }
         auto* func =
-            create<ast::Function>(source, Sym(std::forward<NAME>(name)), std::move(params), type,
+            create<ast::Function>(source, Ident(std::forward<NAME>(name)), std::move(params), type,
                                   block, std::move(attributes), std::move(return_type_attributes));
         AST().AddFunction(func);
         return func;
diff --git a/src/tint/program_builder_test.cc b/src/tint/program_builder_test.cc
index 24e7344..02e3ba2 100644
--- a/src/tint/program_builder_test.cc
+++ b/src/tint/program_builder_test.cc
@@ -60,7 +60,7 @@
     ASSERT_EQ(inner.AST().Functions().Length(), 1u);
     ASSERT_EQ(outer.AST().Functions().Length(), 2u);
     EXPECT_EQ(inner.AST().Functions()[0], outer.AST().Functions()[0]);
-    EXPECT_EQ(outer.AST().Functions()[1]->symbol, outer.Symbols().Get("b"));
+    EXPECT_EQ(outer.AST().Functions()[1]->name->symbol, outer.Symbols().Get("b"));
     EXPECT_EQ(inner.Symbols().Get("a"), outer.Symbols().Get("a"));
     EXPECT_TRUE(inner.Symbols().Get("a").IsValid());
     EXPECT_TRUE(outer.Symbols().Get("a").IsValid());
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 224888e..1a61645 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -5787,7 +5787,7 @@
             });
 
         // Emit call to stub, will be replaced with call to atomic builtin by transform::SpirvAtomic
-        auto* call = builder_.Call(Source{}, stub->symbol, std::move(exprs));
+        auto* call = builder_.Call(Source{}, stub->name->symbol, std::move(exprs));
         if (inst.type_id() != 0) {
             auto* result_type = parser_impl_.ConvertType(inst.type_id());
             TypedExpression expr{result_type, call};
diff --git a/src/tint/reader/wgsl/parser_impl_function_decl_test.cc b/src/tint/reader/wgsl/parser_impl_function_decl_test.cc
index 8fc0a33..9d249f5 100644
--- a/src/tint/reader/wgsl/parser_impl_function_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_function_decl_test.cc
@@ -31,7 +31,7 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
-    EXPECT_EQ(f->symbol, p->builder().Symbols().Get("main"));
+    EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
 
     ASSERT_EQ(f->params.Length(), 2u);
@@ -74,7 +74,7 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
-    EXPECT_EQ(f->symbol, p->builder().Symbols().Get(function_ident));
+    EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get(function_ident));
     EXPECT_EQ(f->return_type, nullptr);
 
     ASSERT_EQ(f->params.Length(), 2u);
@@ -100,7 +100,7 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
-    EXPECT_EQ(f->symbol, p->builder().Symbols().Get("main"));
+    EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
     ASSERT_EQ(f->params.Length(), 0u);
 
@@ -144,7 +144,7 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
-    EXPECT_EQ(f->symbol, p->builder().Symbols().Get("main"));
+    EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
     ASSERT_EQ(f->params.Length(), 0u);
 
@@ -192,7 +192,7 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
-    EXPECT_EQ(f->symbol, p->builder().Symbols().Get("main"));
+    EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
     ASSERT_EQ(f->params.Length(), 0u);
 
@@ -237,7 +237,7 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
-    EXPECT_EQ(f->symbol, p->builder().Symbols().Get("main"));
+    EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     ASSERT_NE(f->return_type, nullptr);
     EXPECT_TRUE(f->return_type->Is<ast::F32>());
     ASSERT_EQ(f->params.Length(), 0u);
diff --git a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
index 435c4ef..c77ba01 100644
--- a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
@@ -192,7 +192,7 @@
 
     auto program = p->program();
     ASSERT_EQ(program.AST().Functions().Length(), 1u);
-    EXPECT_EQ(program.Symbols().NameFor(program.AST().Functions()[0]->symbol), "main");
+    EXPECT_EQ(program.Symbols().NameFor(program.AST().Functions()[0]->name->symbol), "main");
 }
 
 TEST_F(ParserImplTest, GlobalDecl_Function_WithAttribute) {
@@ -202,7 +202,7 @@
 
     auto program = p->program();
     ASSERT_EQ(program.AST().Functions().Length(), 1u);
-    EXPECT_EQ(program.Symbols().NameFor(program.AST().Functions()[0]->symbol), "main");
+    EXPECT_EQ(program.Symbols().NameFor(program.AST().Functions()[0]->name->symbol), "main");
 }
 
 TEST_F(ParserImplTest, GlobalDecl_Function_Invalid) {
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 7326f81..c07eb0d 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -195,7 +195,7 @@
                 TraverseType(alias->type);
             },
             [&](const ast::Function* func) {
-                Declare(func->symbol, func);
+                Declare(func->name->symbol, func);
                 TraverseFunction(func);
             },
             [&](const ast::Variable* var) {
@@ -559,7 +559,7 @@
         return Switch(
             node,  //
             [&](const ast::TypeDecl* td) { return td->name; },
-            [&](const ast::Function* func) { return func->symbol; },
+            [&](const ast::Function* func) { return func->name->symbol; },
             [&](const ast::Variable* var) { return var->symbol; },
             [&](const ast::DiagnosticDirective*) { return Symbol(); },
             [&](const ast::Enable*) { return Symbol(); },
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 947499b..e268107f 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -350,7 +350,7 @@
                         return nullptr;
                     },
                     [&](sem::Function* func) {
-                        auto name = builder_->Symbols().NameFor(func->Declaration()->symbol);
+                        auto name = builder_->Symbols().NameFor(func->Declaration()->name->symbol);
                         AddError("cannot use function '" + name + "' as type", ty->source);
                         AddNote("'" + name + "' declared here", func->Declaration()->source);
                         return nullptr;
@@ -993,6 +993,8 @@
 }
 
 sem::Function* Resolver::Function(const ast::Function* decl) {
+    Mark(decl->name);
+
     uint32_t parameter_index = 0;
     utils::Hashmap<Symbol, Source, 8> parameter_names;
     utils::Vector<sem::Parameter*, 8> parameters;
@@ -1080,9 +1082,9 @@
 
     if (auto* str = return_type->As<sem::Struct>()) {
         if (!ApplyAddressSpaceUsageToType(type::AddressSpace::kNone, str, decl->source)) {
-            AddNote(
-                "while instantiating return type for " + builder_->Symbols().NameFor(decl->symbol),
-                decl->source);
+            AddNote("while instantiating return type for " +
+                        builder_->Symbols().NameFor(decl->name->symbol),
+                    decl->source);
             return nullptr;
         }
 
@@ -1669,7 +1671,7 @@
                 break;
             case Alias::ModuleScope: {
                 auto* func = var.expr->Stmt()->Function();
-                auto func_name = builder_->Symbols().NameFor(func->Declaration()->symbol);
+                auto func_name = builder_->Symbols().NameFor(func->Declaration()->name->symbol);
                 AddNote(
                     "aliases with module-scope variable " + var.access + " in '" + func_name + "'",
                     var.expr->Declaration()->source);
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 21425da..1433a6e 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -2000,17 +2000,17 @@
 
     const auto& b_eps = func_b_sem->AncestorEntryPoints();
     ASSERT_EQ(2u, b_eps.size());
-    EXPECT_EQ(Symbols().Register("ep_1"), b_eps[0]->Declaration()->symbol);
-    EXPECT_EQ(Symbols().Register("ep_2"), b_eps[1]->Declaration()->symbol);
+    EXPECT_EQ(Symbols().Register("ep_1"), b_eps[0]->Declaration()->name->symbol);
+    EXPECT_EQ(Symbols().Register("ep_2"), b_eps[1]->Declaration()->name->symbol);
 
     const auto& a_eps = func_a_sem->AncestorEntryPoints();
     ASSERT_EQ(1u, a_eps.size());
-    EXPECT_EQ(Symbols().Register("ep_1"), a_eps[0]->Declaration()->symbol);
+    EXPECT_EQ(Symbols().Register("ep_1"), a_eps[0]->Declaration()->name->symbol);
 
     const auto& c_eps = func_c_sem->AncestorEntryPoints();
     ASSERT_EQ(2u, c_eps.size());
-    EXPECT_EQ(Symbols().Register("ep_1"), c_eps[0]->Declaration()->symbol);
-    EXPECT_EQ(Symbols().Register("ep_2"), c_eps[1]->Declaration()->symbol);
+    EXPECT_EQ(Symbols().Register("ep_1"), c_eps[0]->Declaration()->name->symbol);
+    EXPECT_EQ(Symbols().Register("ep_2"), c_eps[1]->Declaration()->name->symbol);
 
     EXPECT_TRUE(ep_1_sem->AncestorEntryPoints().empty());
     EXPECT_TRUE(ep_2_sem->AncestorEntryPoints().empty());
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 8fa6d64..bae85a4 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -171,7 +171,7 @@
     /// @param func the AST function
     /// @param builder the program builder
     FunctionInfo(const ast::Function* func, const ProgramBuilder* builder) {
-        name = builder->Symbols().NameFor(func->symbol);
+        name = builder->Symbols().NameFor(func->name->symbol);
         callsite_tag = {CallSiteTag::CallSiteNoRestriction};
         function_tag = NoRestriction;
 
@@ -1769,7 +1769,7 @@
                 if (auto* param = var->As<sem::Parameter>()) {
                     auto* func = param->Owner()->As<sem::Function>();
                     ss << param_type(param) << "'" << NameFor(ident->identifier) << "' of '"
-                       << NameFor(func->Declaration()) << "' may be non-uniform";
+                       << NameFor(func->Declaration()->name) << "' may be non-uniform";
                 } else {
                     ss << "reading from " << var_type(var) << "'" << NameFor(ident->identifier)
                        << "' may result in a non-uniform value";
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 02b8f39..29218af 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -1037,7 +1037,7 @@
         } else if (TINT_UNLIKELY(IsValidationEnabled(
                        decl->attributes, ast::DisabledValidation::kFunctionHasNoBody))) {
             TINT_ICE(Resolver, diagnostics_)
-                << "Function " << symbols_.NameFor(decl->symbol) << " has no body";
+                << "Function " << symbols_.NameFor(decl->name->symbol) << " has no body";
         }
 
         for (auto* attr : decl->return_type_attributes) {
@@ -1069,7 +1069,7 @@
     // a function behavior is always one of {}, or {Next}.
     if (TINT_UNLIKELY(func->Behaviors() != sem::Behaviors{} &&
                       func->Behaviors() != sem::Behavior::kNext)) {
-        auto name = symbols_.NameFor(decl->symbol);
+        auto name = symbols_.NameFor(decl->name->symbol);
         TINT_ICE(Resolver, diagnostics_)
             << "function '" << name << "' behaviors are: " << func->Behaviors();
     }
@@ -1236,30 +1236,30 @@
     };
 
     // Outer lambda for validating the entry point attributes for a type.
-    auto validate_entry_point_attributes = [&](utils::VectorRef<const ast::Attribute*> attrs,
-                                               const type::Type* ty, Source source,
-                                               ParamOrRetType param_or_ret,
-                                               std::optional<uint32_t> location) {
-        if (!validate_entry_point_attributes_inner(attrs, ty, source, param_or_ret,
-                                                   /*is_struct_member*/ false, location)) {
-            return false;
-        }
+    auto validate_entry_point_attributes =
+        [&](utils::VectorRef<const ast::Attribute*> attrs, const type::Type* ty, Source source,
+            ParamOrRetType param_or_ret, std::optional<uint32_t> location) {
+            if (!validate_entry_point_attributes_inner(attrs, ty, source, param_or_ret,
+                                                       /*is_struct_member*/ false, location)) {
+                return false;
+            }
 
-        if (auto* str = ty->As<sem::Struct>()) {
-            for (auto* member : str->Members()) {
-                if (!validate_entry_point_attributes_inner(
-                        member->Declaration()->attributes, member->Type(), member->Source(),
-                        param_or_ret,
-                        /*is_struct_member*/ true, member->Location())) {
-                    AddNote("while analyzing entry point '" + symbols_.NameFor(decl->symbol) + "'",
-                            decl->source);
-                    return false;
+            if (auto* str = ty->As<sem::Struct>()) {
+                for (auto* member : str->Members()) {
+                    if (!validate_entry_point_attributes_inner(
+                            member->Declaration()->attributes, member->Type(), member->Source(),
+                            param_or_ret,
+                            /*is_struct_member*/ true, member->Location())) {
+                        AddNote("while analyzing entry point '" +
+                                    symbols_.NameFor(decl->name->symbol) + "'",
+                                decl->source);
+                        return false;
+                    }
                 }
             }
-        }
 
-        return true;
-    };
+            return true;
+        };
 
     for (auto* param : func->Parameters()) {
         auto* param_decl = param->Declaration();
@@ -1329,7 +1329,7 @@
             // Bindings must not alias within a shader stage: two different variables in the
             // resource interface of a given shader must not have the same group and binding values,
             // when considered as a pair of values.
-            auto func_name = symbols_.NameFor(decl->symbol);
+            auto func_name = symbols_.NameFor(decl->name->symbol);
             AddError(
                 "entry point '" + func_name +
                     "' references multiple variables that use the same resource binding @group(" +
@@ -1875,11 +1875,12 @@
     auto backtrace = [&](const sem::Function* func, const sem::Function* entry_point) {
         if (func != entry_point) {
             TraverseCallChain(diagnostics_, entry_point, func, [&](const sem::Function* f) {
-                AddNote("called by function '" + symbols_.NameFor(f->Declaration()->symbol) + "'",
-                        f->Declaration()->source);
+                AddNote(
+                    "called by function '" + symbols_.NameFor(f->Declaration()->name->symbol) + "'",
+                    f->Declaration()->source);
             });
             AddNote("called by entry point '" +
-                        symbols_.NameFor(entry_point->Declaration()->symbol) + "'",
+                        symbols_.NameFor(entry_point->Declaration()->name->symbol) + "'",
                     entry_point->Declaration()->source);
         }
     };
@@ -1986,7 +1987,7 @@
                     continue;
                 }
 
-                AddError("entry point '" + symbols_.NameFor(ep->Declaration()->symbol) +
+                AddError("entry point '" + symbols_.NameFor(ep->Declaration()->name->symbol) +
                              "' uses two different 'push_constant' variables.",
                          ep->Declaration()->source);
                 AddNote("first 'push_constant' variable declaration is here",
@@ -1994,11 +1995,11 @@
                 if (func != ep) {
                     TraverseCallChain(diagnostics_, ep, func, [&](const sem::Function* f) {
                         AddNote("called by function '" +
-                                    symbols_.NameFor(f->Declaration()->symbol) + "'",
+                                    symbols_.NameFor(f->Declaration()->name->symbol) + "'",
                                 f->Declaration()->source);
                     });
                     AddNote("called by entry point '" +
-                                symbols_.NameFor(ep->Declaration()->symbol) + "'",
+                                symbols_.NameFor(ep->Declaration()->name->symbol) + "'",
                             ep->Declaration()->source);
                 }
                 AddNote("second 'push_constant' variable declaration is here",
@@ -2007,11 +2008,11 @@
                     TraverseCallChain(
                         diagnostics_, ep, push_constant_func, [&](const sem::Function* f) {
                             AddNote("called by function '" +
-                                        symbols_.NameFor(f->Declaration()->symbol) + "'",
+                                        symbols_.NameFor(f->Declaration()->name->symbol) + "'",
                                     f->Declaration()->source);
                         });
                     AddNote("called by entry point '" +
-                                symbols_.NameFor(ep->Declaration()->symbol) + "'",
+                                symbols_.NameFor(ep->Declaration()->name->symbol) + "'",
                             ep->Declaration()->source);
                 }
                 return false;
diff --git a/src/tint/sem/function.cc b/src/tint/sem/function.cc
index 3ce22a1..e7bf889 100644
--- a/src/tint/sem/function.cc
+++ b/src/tint/sem/function.cc
@@ -15,6 +15,7 @@
 #include "src/tint/sem/function.h"
 
 #include "src/tint/ast/function.h"
+#include "src/tint/ast/identifier.h"
 #include "src/tint/sem/variable.h"
 #include "src/tint/type/depth_texture.h"
 #include "src/tint/type/external_texture.h"
@@ -141,7 +142,7 @@
 
 bool Function::HasAncestorEntryPoint(Symbol symbol) const {
     for (const auto* point : ancestor_entry_points_) {
-        if (point->Declaration()->symbol == symbol) {
+        if (point->Declaration()->name->symbol == symbol) {
             return true;
         }
     }
diff --git a/src/tint/transform/calculate_array_length.cc b/src/tint/transform/calculate_array_length.cc
index 7bdc9cd..de36315 100644
--- a/src/tint/transform/calculate_array_length.cc
+++ b/src/tint/transform/calculate_array_length.cc
@@ -107,7 +107,7 @@
             auto* type = CreateASTTypeFor(ctx, buffer_type);
             auto* disable_validation = b.Disable(ast::DisabledValidation::kFunctionParameter);
             b.AST().AddFunction(b.create<ast::Function>(
-                name,
+                b.Ident(name),
                 utils::Vector{
                     b.Param("buffer",
                             b.ty.pointer(type, buffer_type->AddressSpace(), buffer_type->Access()),
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index 4a223f6..9a9d91f 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -542,24 +542,25 @@
         if (cfg.shader_style == ShaderStyle::kGlsl) {
             // In GLSL, clone the original entry point name, as the wrapper will be
             // called "main".
-            inner_name = ctx.Clone(func_ast->symbol);
+            inner_name = ctx.Clone(func_ast->name->symbol);
         } else {
             // Add a suffix to the function name, as the wrapper function will take
             // the original entry point name.
-            auto ep_name = ctx.src->Symbols().NameFor(func_ast->symbol);
+            auto ep_name = ctx.src->Symbols().NameFor(func_ast->name->symbol);
             inner_name = ctx.dst->Symbols().New(ep_name + "_inner");
         }
 
         // Clone everything, dropping the function and return type attributes.
         // The parameter attributes will have already been stripped during
         // processing.
-        auto* inner_function = ctx.dst->create<ast::Function>(
-            inner_name, ctx.Clone(func_ast->params), ctx.Clone(func_ast->return_type),
-            ctx.Clone(func_ast->body), utils::Empty, utils::Empty);
+        auto* inner_function =
+            ctx.dst->create<ast::Function>(ctx.dst->Ident(inner_name), ctx.Clone(func_ast->params),
+                                           ctx.Clone(func_ast->return_type),
+                                           ctx.Clone(func_ast->body), utils::Empty, utils::Empty);
         ctx.Replace(func_ast, inner_function);
 
         // Call the function.
-        return ctx.dst->Call(inner_function->symbol, inner_call_parameters);
+        return ctx.dst->Call(inner_function->name->symbol, inner_call_parameters);
     }
 
     /// Process the entry point function.
@@ -656,12 +657,12 @@
         if (cfg.shader_style == ShaderStyle::kGlsl) {
             name = ctx.dst->Symbols().New("main");
         } else {
-            name = ctx.Clone(func_ast->symbol);
+            name = ctx.Clone(func_ast->name->symbol);
         }
 
         auto* wrapper_func = ctx.dst->create<ast::Function>(
-            name, wrapper_ep_parameters, wrapper_ret_type(), ctx.dst->Block(wrapper_body),
-            ctx.Clone(func_ast->attributes), utils::Empty);
+            ctx.dst->Ident(name), wrapper_ep_parameters, wrapper_ret_type(),
+            ctx.dst->Block(wrapper_body), ctx.Clone(func_ast->attributes), utils::Empty);
         ctx.InsertAfter(ctx.src->AST().GlobalDeclarations(), func_ast, wrapper_func);
     }
 
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index 7a39d6e..8703c90 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -214,12 +214,12 @@
                 }
                 // Create a new function signature that differs only in the parameter
                 // list.
-                auto symbol = ctx.Clone(ast_fn->symbol);
+                auto name = ctx.Clone(ast_fn->name);
                 auto* return_type = ctx.Clone(ast_fn->return_type);
                 auto* body = ctx.Clone(ast_fn->body);
                 auto attributes = ctx.Clone(ast_fn->attributes);
                 auto return_type_attributes = ctx.Clone(ast_fn->return_type_attributes);
-                return ctx.dst->create<ast::Function>(symbol, params, return_type, body,
+                return ctx.dst->create<ast::Function>(name, params, return_type, body,
                                                       std::move(attributes),
                                                       std::move(return_type_attributes));
             }
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index ef0ce52..fc2003c 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -483,7 +483,7 @@
                 if (auto* intrinsic = IntrinsicLoadFor(ctx.dst, address_space, el_ty)) {
                     auto* el_ast_ty = CreateASTTypeFor(ctx, el_ty);
                     auto* func = b.create<ast::Function>(
-                        name, params, el_ast_ty, nullptr,
+                        b.Ident(name), params, el_ast_ty, nullptr,
                         utils::Vector{
                             intrinsic,
                             b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
@@ -582,7 +582,7 @@
 
                 if (auto* intrinsic = IntrinsicStoreFor(ctx.dst, address_space, el_ty)) {
                     auto* func = b.create<ast::Function>(
-                        name, params, b.ty.void_(), nullptr,
+                        b.Ident(name), params, b.ty.void_(), nullptr,
                         utils::Vector{
                             intrinsic,
                             b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
@@ -728,7 +728,8 @@
             }
 
             auto* func = b.create<ast::Function>(
-                b.Symbols().New(std::string{"tint_"} + intrinsic->str()), params, ret_ty, nullptr,
+                b.Ident(b.Symbols().New(std::string{"tint_"} + intrinsic->str())), params, ret_ty,
+                nullptr,
                 utils::Vector{
                     atomic,
                     b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
@@ -736,7 +737,7 @@
                 utils::Empty);
 
             b.AST().AddFunction(func);
-            return func->symbol;
+            return func->name->symbol;
         });
     }
 };
diff --git a/src/tint/transform/direct_variable_access.cc b/src/tint/transform/direct_variable_access.cc
index 84deea9..3522086 100644
--- a/src/tint/transform/direct_variable_access.cc
+++ b/src/tint/transform/direct_variable_access.cc
@@ -600,7 +600,7 @@
 
             // Function was not called. Create a single variant with an empty signature.
             FnVariant variant;
-            variant.name = ctx.Clone(fn->Declaration()->symbol);
+            variant.name = ctx.Clone(fn->Declaration()->name->symbol);
             variant.order = 0;  // Unaltered comes first.
             fn_info->variants.Add(FnVariant::Signature{}, std::move(variant));
         }
@@ -679,7 +679,7 @@
                 if (target_signature.IsEmpty()) {
                     // Call target does not require any argument changes.
                     FnVariant variant;
-                    variant.name = ctx.Clone(target->Declaration()->symbol);
+                    variant.name = ctx.Clone(target->Declaration()->name->symbol);
                     variant.order = 0;  // Unaltered comes first.
                     return variant;
                 }
@@ -688,7 +688,7 @@
                 // This is derived from the original function name and the pointer parameter
                 // chains.
                 std::stringstream ss;
-                ss << ctx.src->Symbols().NameFor(target->Declaration()->symbol);
+                ss << ctx.src->Symbols().NameFor(target->Declaration()->name->symbol);
                 for (auto* param : target->Parameters()) {
                     if (auto indices = target_signature.Find(param)) {
                         ss << "_" << AccessShapeName(*indices);
@@ -855,7 +855,7 @@
                 auto attrs = ctx.Clone(fn->Declaration()->attributes);
                 auto ret_attrs = ctx.Clone(fn->Declaration()->return_type_attributes);
                 pending_variant =
-                    b.create<ast::Function>(variant.name, std::move(params), ret_ty, body,
+                    b.create<ast::Function>(b.Ident(variant.name), std::move(params), ret_ty, body,
                                             std::move(attrs), std::move(ret_attrs));
             }
 
diff --git a/src/tint/transform/single_entry_point.cc b/src/tint/transform/single_entry_point.cc
index 3fe3be2..8a8daeb 100644
--- a/src/tint/transform/single_entry_point.cc
+++ b/src/tint/transform/single_entry_point.cc
@@ -49,7 +49,7 @@
         if (!f->IsEntryPoint()) {
             continue;
         }
-        if (src->Symbols().NameFor(f->symbol) == cfg->entry_point_name) {
+        if (src->Symbols().NameFor(f->name->symbol) == cfg->entry_point_name) {
             entry_point = f;
             break;
         }
@@ -109,7 +109,7 @@
                 b.AST().AddGlobalVariable(ctx.Clone(c));
             },
             [&](const ast::Function* func) {
-                if (sem.Get(func)->HasAncestorEntryPoint(entry_point->symbol)) {
+                if (sem.Get(func)->HasAncestorEntryPoint(entry_point->name->symbol)) {
                     b.AST().AddFunction(ctx.Clone(func));
                 }
             },
diff --git a/src/tint/transform/vertex_pulling.cc b/src/tint/transform/vertex_pulling.cc
index c75b81c..0566815 100644
--- a/src/tint/transform/vertex_pulling.cc
+++ b/src/tint/transform/vertex_pulling.cc
@@ -921,14 +921,14 @@
         }
 
         // Rewrite the function header with the new parameters.
-        auto func_sym = ctx.Clone(func->symbol);
+        auto func_sym = ctx.Clone(func->name->symbol);
         auto* ret_type = ctx.Clone(func->return_type);
         auto* body = ctx.Clone(func->body);
         auto attrs = ctx.Clone(func->attributes);
         auto ret_attrs = ctx.Clone(func->return_type_attributes);
         auto* new_func =
-            b.create<ast::Function>(func->source, func_sym, new_function_parameters, ret_type, body,
-                                    std::move(attrs), std::move(ret_attrs));
+            b.create<ast::Function>(func->source, b.Ident(func_sym), new_function_parameters,
+                                    ret_type, body, std::move(attrs), std::move(ret_attrs));
         ctx.Replace(func, new_func);
     }
 };
diff --git a/src/tint/writer/glsl/generator.cc b/src/tint/writer/glsl/generator.cc
index 4b1e516..bbe6380 100644
--- a/src/tint/writer/glsl/generator.cc
+++ b/src/tint/writer/glsl/generator.cc
@@ -52,7 +52,7 @@
     // Collect the list of entry points in the sanitized program.
     for (auto* func : sanitized_result.program.AST().Functions()) {
         if (func->IsEntryPoint()) {
-            auto name = sanitized_result.program.Symbols().NameFor(func->symbol);
+            auto name = sanitized_result.program.Symbols().NameFor(func->name->symbol);
             result.entry_points.push_back({name, func->PipelineStage()});
         }
     }
diff --git a/src/tint/writer/glsl/generator_bench.cc b/src/tint/writer/glsl/generator_bench.cc
index dddb303..5a8e277 100644
--- a/src/tint/writer/glsl/generator_bench.cc
+++ b/src/tint/writer/glsl/generator_bench.cc
@@ -14,6 +14,7 @@
 
 #include <string>
 
+#include "src/tint/ast/identifier.h"
 #include "src/tint/ast/module.h"
 #include "src/tint/bench/benchmark.h"
 
@@ -30,7 +31,7 @@
     std::vector<std::string> entry_points;
     for (auto& fn : program.AST().Functions()) {
         if (fn->IsEntryPoint()) {
-            entry_points.emplace_back(program.Symbols().NameFor(fn->symbol));
+            entry_points.emplace_back(program.Symbols().NameFor(fn->name->symbol));
         }
     }
 
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 381230c..597ac02 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -1882,7 +1882,7 @@
 
     {
         auto out = line();
-        auto name = builder_.Symbols().NameFor(func->symbol);
+        auto name = builder_.Symbols().NameFor(func->name->symbol);
         if (!EmitType(out, sem->ReturnType(), type::AddressSpace::kNone, type::Access::kReadWrite,
                       "")) {
             return false;
@@ -2261,7 +2261,8 @@
     {
         auto out = line();
         if (!EmitTypeAndName(out, func_sem->ReturnType(), type::AddressSpace::kUndefined,
-                             type::Access::kUndefined, builder_.Symbols().NameFor(func->symbol))) {
+                             type::Access::kUndefined,
+                             builder_.Symbols().NameFor(func->name->symbol))) {
             return false;
         }
         out << "(";
diff --git a/src/tint/writer/hlsl/generator.cc b/src/tint/writer/hlsl/generator.cc
index b514c2b..788c5e8 100644
--- a/src/tint/writer/hlsl/generator.cc
+++ b/src/tint/writer/hlsl/generator.cc
@@ -51,7 +51,7 @@
     // Collect the list of entry points in the sanitized program.
     for (auto* func : sanitized_result.program.AST().Functions()) {
         if (func->IsEntryPoint()) {
-            auto name = sanitized_result.program.Symbols().NameFor(func->symbol);
+            auto name = sanitized_result.program.Symbols().NameFor(func->name->symbol);
             result.entry_points.push_back({name, func->PipelineStage()});
         }
     }
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 45fa593..21266c3 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -923,7 +923,7 @@
         }
     }
 
-    out << builder_.Symbols().NameFor(func->Declaration()->symbol) << "(";
+    out << builder_.Symbols().NameFor(func->Declaration()->name->symbol) << "(";
 
     bool first = true;
     for (auto* arg : call->Arguments()) {
@@ -1585,7 +1585,7 @@
     const sem::Function* sem_func = builder_.Sem().Get(func);
     auto* result_ty = sem_func->ReturnType();
     const auto& params = sem_func->Parameters();
-    const auto name = builder_.Symbols().NameFor(func->symbol);
+    const auto name = builder_.Symbols().NameFor(func->name->symbol);
     auto& buf = *current_buffer_;
 
     auto rmw = [&](const char* hlsl) -> bool {
@@ -2875,7 +2875,7 @@
 
     {
         auto out = line();
-        auto name = builder_.Symbols().NameFor(func->symbol);
+        auto name = builder_.Symbols().NameFor(func->name->symbol);
         // If the function returns an array, then we need to declare a typedef for
         // this.
         if (sem->ReturnType()->Is<type::Array>()) {
@@ -3239,7 +3239,8 @@
         }
 
         if (!EmitTypeAndName(out, func_sem->ReturnType(), type::AddressSpace::kUndefined,
-                             type::Access::kUndefined, builder_.Symbols().NameFor(func->symbol))) {
+                             type::Access::kUndefined,
+                             builder_.Symbols().NameFor(func->name->symbol))) {
             return false;
         }
         out << "(";
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index b1e9856..f41a4b3 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -1873,7 +1873,7 @@
         if (!EmitType(out, func_sem->ReturnType(), "")) {
             return false;
         }
-        out << " " << program_->Symbols().NameFor(func->symbol) << "(";
+        out << " " << program_->Symbols().NameFor(func->name->symbol) << "(";
 
         bool first = true;
         for (auto* v : func->params) {
@@ -1975,7 +1975,7 @@
 bool GeneratorImpl::EmitEntryPointFunction(const ast::Function* func) {
     auto* func_sem = builder_.Sem().Get(func);
 
-    auto func_name = program_->Symbols().NameFor(func->symbol);
+    auto func_name = program_->Symbols().NameFor(func->name->symbol);
 
     // Returns the binding index of a variable, requiring that the group
     // attribute have a value of zero.
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index c8f32bd..8053147 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -500,7 +500,7 @@
     }
 
     OperandList operands = {Operand(stage), Operand(id),
-                            Operand(builder_.Symbols().NameFor(func->symbol))};
+                            Operand(builder_.Symbols().NameFor(func->name->symbol))};
 
     auto* func_sem = builder_.Sem().Get(func);
     for (const auto* var : func_sem->TransitivelyReferencedGlobals()) {
@@ -602,7 +602,7 @@
     auto func_id = std::get<uint32_t>(func_op);
 
     push_debug(spv::Op::OpName,
-               {Operand(func_id), Operand(builder_.Symbols().NameFor(func_ast->symbol))});
+               {Operand(func_id), Operand(builder_.Symbols().NameFor(func_ast->name->symbol))});
 
     auto ret_id = GenerateTypeIfNeeded(func->ReturnType());
     if (ret_id == 0) {
@@ -661,7 +661,7 @@
         }
     }
 
-    func_symbol_to_id_[func_ast->symbol] = func_id;
+    func_symbol_to_id_[func_ast->name->symbol] = func_id;
 
     return true;
 }
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index a4c1bb3..6a56a21 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -308,7 +308,7 @@
     }
     {
         auto out = line();
-        out << "fn " << program_->Symbols().NameFor(func->symbol) << "(";
+        out << "fn " << program_->Symbols().NameFor(func->name->symbol) << "(";
 
         bool first = true;
         for (auto* v : func->params) {