tint/sem: Make BindingPoint optional

Reduces hops from sem -> ast, just to know whether the variable has a binding point.

Change-Id: I5620198e6f08b73d5a0171d95874f1a2dae5d93e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127060
Reviewed-by: James Price <jrprice@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/generate_external_texture_bindings.cc b/src/tint/cmd/generate_external_texture_bindings.cc
index 1b6ca74..52650ea 100644
--- a/src/tint/cmd/generate_external_texture_bindings.cc
+++ b/src/tint/cmd/generate_external_texture_bindings.cc
@@ -36,12 +36,13 @@
     std::vector<sem::BindingPoint> ext_tex_bps;
     for (auto* var : program->AST().GlobalVariables()) {
         if (auto* sem_var = program->Sem().Get(var)->As<sem::GlobalVariable>()) {
-            auto bp = sem_var->BindingPoint();
-            auto& n = group_to_next_binding_number[bp.group];
-            n = std::max(n, bp.binding + 1);
+            if (auto bp = sem_var->BindingPoint()) {
+                auto& n = group_to_next_binding_number[bp->group];
+                n = std::max(n, bp->binding + 1);
 
-            if (sem_var->Type()->UnwrapRef()->Is<type::ExternalTexture>()) {
-                ext_tex_bps.emplace_back(bp);
+                if (sem_var->Type()->UnwrapRef()->Is<type::ExternalTexture>()) {
+                    ext_tex_bps.emplace_back(*bp);
+                }
             }
         }
     }
diff --git a/src/tint/fuzzers/tint_common_fuzzer.cc b/src/tint/fuzzers/tint_common_fuzzer.cc
index 6be9b98..7cbc8bf 100644
--- a/src/tint/fuzzers/tint_common_fuzzer.cc
+++ b/src/tint/fuzzers/tint_common_fuzzer.cc
@@ -271,12 +271,13 @@
         std::vector<sem::BindingPoint> ext_tex_bps;
         for (auto* var : program.AST().GlobalVariables()) {
             if (auto* sem_var = program.Sem().Get(var)->As<sem::GlobalVariable>()) {
-                auto bp = sem_var->BindingPoint();
-                auto& n = group_to_next_binding_number[bp.group];
-                n = std::max(n, bp.binding + 1);
+                if (auto bp = sem_var->BindingPoint()) {
+                    auto& n = group_to_next_binding_number[bp->group];
+                    n = std::max(n, bp->binding + 1);
 
-                if (sem_var->Type()->UnwrapRef()->Is<type::ExternalTexture>()) {
-                    ext_tex_bps.emplace_back(bp);
+                    if (sem_var->Type()->UnwrapRef()->Is<type::ExternalTexture>()) {
+                        ext_tex_bps.emplace_back(*bp);
+                    }
                 }
             }
         }
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index d2330e9..f2749f3 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -531,8 +531,8 @@
         auto* texture = pair.first->As<sem::GlobalVariable>();
         auto* sampler = pair.second ? pair.second->As<sem::GlobalVariable>() : nullptr;
         SamplerTexturePair new_pair;
-        new_pair.sampler_binding_point = sampler ? sampler->BindingPoint() : placeholder;
-        new_pair.texture_binding_point = texture->BindingPoint();
+        new_pair.sampler_binding_point = sampler ? *sampler->BindingPoint() : placeholder;
+        new_pair.texture_binding_point = *texture->BindingPoint();
         new_pairs.push_back(new_pair);
     }
     return new_pairs;
@@ -834,8 +834,8 @@
 
         GetOriginatingResources(std::array<const ast::Expression*, 2>{t, s},
                                 [&](std::array<const sem::GlobalVariable*, 2> globals) {
-                                    auto texture_binding_point = globals[0]->BindingPoint();
-                                    auto sampler_binding_point = globals[1]->BindingPoint();
+                                    auto texture_binding_point = *globals[0]->BindingPoint();
+                                    auto sampler_binding_point = *globals[1]->BindingPoint();
 
                                     for (auto* entry_point : entry_points) {
                                         const auto& ep_name =
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 5aac00c..bfe45c9 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -276,7 +276,7 @@
         sem = builder_->create<sem::GlobalVariable>(
             v, ty, sem::EvaluationStage::kRuntime, builtin::AddressSpace::kUndefined,
             builtin::Access::kUndefined,
-            /* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
+            /* constant_value */ nullptr, std::nullopt, std::nullopt);
     } else {
         sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime,
                                                    builtin::AddressSpace::kUndefined,
@@ -336,7 +336,7 @@
     auto* sem = builder_->create<sem::GlobalVariable>(
         v, ty, sem::EvaluationStage::kOverride, builtin::AddressSpace::kUndefined,
         builtin::Access::kUndefined,
-        /* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
+        /* constant_value */ nullptr, std::nullopt, std::nullopt);
     sem->SetInitializer(rhs);
 
     if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
@@ -430,7 +430,7 @@
     auto* sem = is_global
                     ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
                           c, ty, sem::EvaluationStage::kConstant, builtin::AddressSpace::kUndefined,
-                          builtin::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
+                          builtin::Access::kUndefined, value, std::nullopt, std::nullopt))
                     : static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
                           c, ty, sem::EvaluationStage::kConstant, builtin::AddressSpace::kUndefined,
                           builtin::Access::kUndefined, current_statement_, value));
@@ -528,7 +528,7 @@
 
     sem::Variable* sem = nullptr;
     if (is_global) {
-        sem::BindingPoint binding_point;
+        std::optional<sem::BindingPoint> binding_point;
         if (var->HasBindingPoint()) {
             uint32_t binding = 0;
             {
@@ -640,8 +640,9 @@
         }
     }
 
-    sem::BindingPoint binding_point;
+    std::optional<sem::BindingPoint> binding_point;
     if (param->HasBindingPoint()) {
+        binding_point = sem::BindingPoint{};
         {
             ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@binding value"};
             TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
@@ -651,7 +652,7 @@
             if (!materialized) {
                 return nullptr;
             }
-            binding_point.binding = materialized->ConstantValue()->ValueAs<u32>();
+            binding_point->binding = materialized->ConstantValue()->ValueAs<u32>();
         }
         {
             ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@group value"};
@@ -662,7 +663,7 @@
             if (!materialized) {
                 return nullptr;
             }
-            binding_point.group = materialized->ConstantValue()->ValueAs<u32>();
+            binding_point->group = materialized->ConstantValue()->ValueAs<u32>();
         }
     }
 
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 65d7310..36d5139 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -1346,11 +1346,14 @@
     utils::Hashmap<sem::BindingPoint, const ast::Variable*, 8> binding_points;
     for (auto* global : func->TransitivelyReferencedGlobals()) {
         auto* var_decl = global->Declaration()->As<ast::Var>();
-        if (!var_decl || !var_decl->HasBindingPoint()) {
+        if (!var_decl) {
             continue;
         }
         auto bp = global->BindingPoint();
-        if (auto added = binding_points.Add(bp, var_decl);
+        if (!bp) {
+            continue;
+        }
+        if (auto added = binding_points.Add(*bp, var_decl);
             !added &&
             IsValidationEnabled(decl->attributes,
                                 ast::DisabledValidation::kBindingPointCollision) &&
@@ -1364,7 +1367,7 @@
             AddError(
                 "entry point '" + func_name +
                     "' references multiple variables that use the same resource binding @group(" +
-                    std::to_string(bp.group) + "), @binding(" + std::to_string(bp.binding) + ")",
+                    std::to_string(bp->group) + "), @binding(" + std::to_string(bp->binding) + ")",
                 var_decl->source);
             AddNote("first resource binding usage declared here", (*added.value)->source);
             return false;
diff --git a/src/tint/sem/function.cc b/src/tint/sem/function.cc
index ec51b7b..9484012 100644
--- a/src/tint/sem/function.cc
+++ b/src/tint/sem/function.cc
@@ -60,8 +60,8 @@
             continue;
         }
 
-        if (global->Declaration()->HasBindingPoint()) {
-            ret.push_back({global, global->BindingPoint()});
+        if (auto bp = global->BindingPoint()) {
+            ret.push_back({global, *bp});
         }
     }
     return ret;
@@ -75,8 +75,8 @@
             continue;
         }
 
-        if (global->Declaration()->HasBindingPoint()) {
-            ret.push_back({global, global->BindingPoint()});
+        if (auto bp = global->BindingPoint()) {
+            ret.push_back({global, *bp});
         }
     }
     return ret;
@@ -119,8 +119,8 @@
     for (auto* global : TransitivelyReferencedGlobals()) {
         auto* unwrapped_type = global->Type()->UnwrapRef();
         if (unwrapped_type->TypeInfo().Is(type)) {
-            if (global->Declaration()->HasBindingPoint()) {
-                ret.push_back({global, global->BindingPoint()});
+            if (auto bp = global->BindingPoint()) {
+                ret.push_back({global, *bp});
             }
         }
     }
@@ -147,8 +147,8 @@
             continue;
         }
 
-        if (global->Declaration()->HasBindingPoint()) {
-            ret.push_back({global, global->BindingPoint()});
+        if (auto bp = global->BindingPoint()) {
+            ret.push_back({global, *bp});
         }
     }
     return ret;
@@ -172,8 +172,8 @@
             continue;
         }
 
-        if (global->Declaration()->HasBindingPoint()) {
-            ret.push_back({global, global->BindingPoint()});
+        if (auto bp = global->BindingPoint()) {
+            ret.push_back({global, *bp});
         }
     }
 
diff --git a/src/tint/sem/variable.cc b/src/tint/sem/variable.cc
index 8f8248c..2e3cb88 100644
--- a/src/tint/sem/variable.cc
+++ b/src/tint/sem/variable.cc
@@ -61,7 +61,7 @@
                                builtin::AddressSpace address_space,
                                builtin::Access access,
                                const constant::Value* constant_value,
-                               sem::BindingPoint binding_point,
+                               std::optional<sem::BindingPoint> binding_point,
                                std::optional<uint32_t> location)
     : Base(declaration, type, stage, address_space, access, constant_value),
       binding_point_(binding_point),
@@ -75,7 +75,7 @@
                      builtin::AddressSpace address_space,
                      builtin::Access access,
                      const ParameterUsage usage /* = ParameterUsage::kNone */,
-                     sem::BindingPoint binding_point /* = {} */,
+                     std::optional<sem::BindingPoint> binding_point /* = {} */,
                      std::optional<uint32_t> location /* = std::nullopt */)
     : Base(declaration, type, EvaluationStage::kRuntime, address_space, access, nullptr),
       index_(index),
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index 701a3f1..647a77b 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -165,14 +165,14 @@
                    builtin::AddressSpace address_space,
                    builtin::Access access,
                    const constant::Value* constant_value,
-                   sem::BindingPoint binding_point = {},
+                   std::optional<sem::BindingPoint> binding_point = std::nullopt,
                    std::optional<uint32_t> location = std::nullopt);
 
     /// Destructor
     ~GlobalVariable() override;
 
     /// @returns the resource binding point for the variable
-    sem::BindingPoint BindingPoint() const { return binding_point_; }
+    std::optional<sem::BindingPoint> BindingPoint() const { return binding_point_; }
 
     /// @param id the constant identifier to assign to this variable
     void SetOverrideId(OverrideId id) { override_id_ = id; }
@@ -184,7 +184,7 @@
     std::optional<uint32_t> Location() const { return location_; }
 
   private:
-    const sem::BindingPoint binding_point_;
+    const std::optional<sem::BindingPoint> binding_point_;
 
     tint::OverrideId override_id_;
     std::optional<uint32_t> location_;
@@ -208,7 +208,7 @@
               builtin::AddressSpace address_space,
               builtin::Access access,
               const ParameterUsage usage = ParameterUsage::kNone,
-              sem::BindingPoint binding_point = {},
+              std::optional<sem::BindingPoint> binding_point = {},
               std::optional<uint32_t> location = std::nullopt);
 
     /// Destructor
@@ -239,7 +239,7 @@
     void SetShadows(const CastableBase* shadows) { shadows_ = shadows; }
 
     /// @returns the resource binding point for the parameter
-    sem::BindingPoint BindingPoint() const { return binding_point_; }
+    std::optional<sem::BindingPoint> BindingPoint() const { return binding_point_; }
 
     /// @returns the location value for the parameter, if set
     std::optional<uint32_t> Location() const { return location_; }
@@ -249,7 +249,7 @@
     const ParameterUsage usage_;
     CallTarget const* owner_ = nullptr;
     const CastableBase* shadows_ = nullptr;
-    const sem::BindingPoint binding_point_;
+    const std::optional<sem::BindingPoint> binding_point_;
     const std::optional<uint32_t> location_;
 };
 
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index a396965..e0358b4 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -82,13 +82,14 @@
 
         IterateArrayLengthOnStorageVar([&](const ast::CallExpression*, const sem::VariableUser*,
                                            const sem::GlobalVariable* var) {
-            auto binding = var->BindingPoint();
-            auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
-            if (idx_itr == cfg->bindpoint_to_size_index.end()) {
-                return;
-            }
-            if (idx_itr->second > max_buffer_size_index) {
-                max_buffer_size_index = idx_itr->second;
+            if (auto binding = var->BindingPoint()) {
+                auto idx_itr = cfg->bindpoint_to_size_index.find(*binding);
+                if (idx_itr == cfg->bindpoint_to_size_index.end()) {
+                    return;
+                }
+                if (idx_itr->second > max_buffer_size_index) {
+                    max_buffer_size_index = idx_itr->second;
+                }
             }
         });
 
@@ -120,7 +121,10 @@
                                            const sem::VariableUser* storage_buffer_sem,
                                            const sem::GlobalVariable* var) {
             auto binding = var->BindingPoint();
-            auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
+            if (!binding) {
+                return;
+            }
+            auto idx_itr = cfg->bindpoint_to_size_index.find(*binding);
             if (idx_itr == cfg->bindpoint_to_size_index.end()) {
                 return;
             }
diff --git a/src/tint/transform/binding_remapper.cc b/src/tint/transform/binding_remapper.cc
index ecbd5cd..6b1888d 100644
--- a/src/tint/transform/binding_remapper.cc
+++ b/src/tint/transform/binding_remapper.cc
@@ -71,10 +71,8 @@
             auto* func = src->Sem().Get(func_ast);
             std::unordered_map<sem::BindingPoint, int> binding_point_counts;
             for (auto* global : func->TransitivelyReferencedGlobals()) {
-                if (global->Declaration()->HasBindingPoint()) {
-                    BindingPoint from = global->BindingPoint();
-
-                    auto bp_it = remappings->binding_points.find(from);
+                if (auto from = global->BindingPoint()) {
+                    auto bp_it = remappings->binding_points.find(*from);
                     if (bp_it != remappings->binding_points.end()) {
                         // Remapped
                         BindingPoint to = bp_it->second;
@@ -83,8 +81,8 @@
                         }
                     } else {
                         // No remapping
-                        if (binding_point_counts[from]++) {
-                            add_collision_attr.emplace(from);
+                        if (binding_point_counts[*from]++) {
+                            add_collision_attr.emplace(*from);
                         }
                     }
                 }
@@ -97,7 +95,7 @@
             auto* global_sem = src->Sem().Get<sem::GlobalVariable>(var);
 
             // The original binding point
-            BindingPoint from = global_sem->BindingPoint();
+            BindingPoint from = *global_sem->BindingPoint();
 
             // The binding point after remapping
             BindingPoint bp = from;
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index aa1a686..3e9aa7f 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -107,10 +107,10 @@
                                               const sem::Variable* sampler_var,
                                               std::string name) {
         SamplerTexturePair bp_pair;
-        bp_pair.texture_binding_point = texture_var->As<sem::GlobalVariable>()->BindingPoint();
-        bp_pair.sampler_binding_point = sampler_var
-                                            ? sampler_var->As<sem::GlobalVariable>()->BindingPoint()
-                                            : binding_info->placeholder_binding_point;
+        bp_pair.texture_binding_point = *texture_var->As<sem::GlobalVariable>()->BindingPoint();
+        bp_pair.sampler_binding_point =
+            sampler_var ? *sampler_var->As<sem::GlobalVariable>()->BindingPoint()
+                        : binding_info->placeholder_binding_point;
         auto it = binding_info->binding_map.find(bp_pair);
         if (it != binding_info->binding_map.end()) {
             name = it->second;
@@ -161,9 +161,8 @@
             if (tint::IsAnyOf<type::Texture, type::Sampler>(type) &&
                 !type->Is<type::StorageTexture>()) {
                 ctx.Remove(ctx.src->AST().GlobalDeclarations(), global);
-            } else if (global->HasBindingPoint()) {
-                auto binding_point = global_sem->BindingPoint();
-                if (binding_point.group == 0 && binding_point.binding == 0) {
+            } else if (auto binding_point = global_sem->BindingPoint()) {
+                if (binding_point->group == 0 && binding_point->binding == 0) {
                     auto* attribute =
                         ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision);
                     ctx.InsertFront(global->attributes, attribute);
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index dcf1e1c..2105781 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -115,7 +115,7 @@
             // The binding points for the newly introduced bindings must have been provided to this
             // transform. We fetch the new binding points by providing the original texture_external
             // binding points into the passed map.
-            sem::BindingPoint bp = sem_var->BindingPoint();
+            sem::BindingPoint bp = *sem_var->BindingPoint();
 
             BindingsMap::const_iterator it = new_binding_points->bindings_map.find(bp);
             if (it == new_binding_points->bindings_map.end()) {
diff --git a/src/tint/transform/num_workgroups_from_uniform.cc b/src/tint/transform/num_workgroups_from_uniform.cc
index e4bb05e..18889f4 100644
--- a/src/tint/transform/num_workgroups_from_uniform.cc
+++ b/src/tint/transform/num_workgroups_from_uniform.cc
@@ -148,11 +148,10 @@
                 group = 0;
 
                 for (auto* global : src->AST().GlobalVariables()) {
-                    if (global->HasBindingPoint()) {
-                        auto* global_sem = src->Sem().Get<sem::GlobalVariable>(global);
-                        auto binding_point = global_sem->BindingPoint();
-                        if (binding_point.group >= group) {
-                            group = binding_point.group + 1;
+                    auto* global_sem = src->Sem().Get<sem::GlobalVariable>(global);
+                    if (auto bp = global_sem->BindingPoint()) {
+                        if (bp->group >= group) {
+                            group = bp->group + 1;
                         }
                     }
                 }
diff --git a/src/tint/writer/flatten_bindings_test.cc b/src/tint/writer/flatten_bindings_test.cc
index 7e53356..d1351d8 100644
--- a/src/tint/writer/flatten_bindings_test.cc
+++ b/src/tint/writer/flatten_bindings_test.cc
@@ -67,18 +67,18 @@
 
     auto* sem = flattened->Sem().Get<sem::GlobalVariable>(vars[0]);
     ASSERT_NE(sem, nullptr);
-    EXPECT_EQ(sem->BindingPoint().group, 0u);
-    EXPECT_EQ(sem->BindingPoint().binding, 0u);
+    EXPECT_EQ(sem->BindingPoint()->group, 0u);
+    EXPECT_EQ(sem->BindingPoint()->binding, 0u);
 
     sem = flattened->Sem().Get<sem::GlobalVariable>(vars[1]);
     ASSERT_NE(sem, nullptr);
-    EXPECT_EQ(sem->BindingPoint().group, 0u);
-    EXPECT_EQ(sem->BindingPoint().binding, 1u);
+    EXPECT_EQ(sem->BindingPoint()->group, 0u);
+    EXPECT_EQ(sem->BindingPoint()->binding, 1u);
 
     sem = flattened->Sem().Get<sem::GlobalVariable>(vars[2]);
     ASSERT_NE(sem, nullptr);
-    EXPECT_EQ(sem->BindingPoint().group, 0u);
-    EXPECT_EQ(sem->BindingPoint().binding, 2u);
+    EXPECT_EQ(sem->BindingPoint()->group, 0u);
+    EXPECT_EQ(sem->BindingPoint()->binding, 2u);
 }
 
 TEST_F(FlattenBindingsTest, NotFlat_MultipleNamespaces) {
@@ -131,20 +131,20 @@
     for (size_t i = 0; i < num_buffers; ++i) {
         auto* sem = flattened->Sem().Get<sem::GlobalVariable>(vars[i]);
         ASSERT_NE(sem, nullptr);
-        EXPECT_EQ(sem->BindingPoint().group, 0u);
-        EXPECT_EQ(sem->BindingPoint().binding, i);
+        EXPECT_EQ(sem->BindingPoint()->group, 0u);
+        EXPECT_EQ(sem->BindingPoint()->binding, i);
     }
     for (size_t i = 0; i < num_samplers; ++i) {
         auto* sem = flattened->Sem().Get<sem::GlobalVariable>(vars[i + num_buffers]);
         ASSERT_NE(sem, nullptr);
-        EXPECT_EQ(sem->BindingPoint().group, 0u);
-        EXPECT_EQ(sem->BindingPoint().binding, i);
+        EXPECT_EQ(sem->BindingPoint()->group, 0u);
+        EXPECT_EQ(sem->BindingPoint()->binding, i);
     }
     for (size_t i = 0; i < num_textures; ++i) {
         auto* sem = flattened->Sem().Get<sem::GlobalVariable>(vars[i + num_buffers + num_samplers]);
         ASSERT_NE(sem, nullptr);
-        EXPECT_EQ(sem->BindingPoint().group, 0u);
-        EXPECT_EQ(sem->BindingPoint().binding, i);
+        EXPECT_EQ(sem->BindingPoint()->group, 0u);
+        EXPECT_EQ(sem->BindingPoint()->binding, i);
     }
 }
 
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 4f46e66..72eb737 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -2031,7 +2031,7 @@
         TINT_ICE(Writer, builder_.Diagnostics()) << "storage variable must be of struct type";
         return false;
     }
-    auto bp = sem->As<sem::GlobalVariable>()->BindingPoint();
+    auto bp = *sem->As<sem::GlobalVariable>()->BindingPoint();
     {
         auto out = line();
         out << "layout(binding = " << bp.binding << ", std140";
@@ -2052,7 +2052,7 @@
         TINT_ICE(Writer, builder_.Diagnostics()) << "storage variable must be of struct type";
         return false;
     }
-    auto bp = sem->As<sem::GlobalVariable>()->BindingPoint();
+    auto bp = *sem->As<sem::GlobalVariable>()->BindingPoint();
     line() << "layout(binding = " << bp.binding << ", std430) buffer "
            << UniqueIdentifier(StructName(str) + "_ssbo") << " {";
     EmitStructMembers(current_buffer_, str);
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index b37ebdc..8b9bde8 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -3066,7 +3066,7 @@
 }
 
 bool GeneratorImpl::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
-    auto binding_point = sem->As<sem::GlobalVariable>()->BindingPoint();
+    auto binding_point = *sem->As<sem::GlobalVariable>()->BindingPoint();
     auto* type = sem->Type()->UnwrapRef();
     auto name = var->name->symbol.Name();
     line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
@@ -3095,7 +3095,7 @@
 
     auto* global_sem = sem->As<sem::GlobalVariable>();
     out << RegisterAndSpace(sem->Access() == builtin::Access::kRead ? 't' : 'u',
-                            global_sem->BindingPoint())
+                            *global_sem->BindingPoint())
         << ";";
 
     return true;
@@ -3124,14 +3124,14 @@
 
     if (register_space) {
         auto bp = sem->As<sem::GlobalVariable>()->BindingPoint();
-        out << " : register(" << register_space << bp.binding;
+        out << " : register(" << register_space << bp->binding;
         // Omit the space if it's 0, as it's the default.
         // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better
         // compatibility.
-        if (bp.group == 0) {
+        if (bp->group == 0) {
             out << ")";
         } else {
-            out << ", space" << bp.group << ")";
+            out << ", space" << bp->group << ")";
         }
     }
 
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 842f6cb..f86a144 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -1996,12 +1996,12 @@
         }
         auto* param_sem = program_->Sem().Get<sem::Parameter>(param);
         auto bp = param_sem->BindingPoint();
-        if (TINT_UNLIKELY(bp.group != 0)) {
+        if (TINT_UNLIKELY(bp->group != 0)) {
             TINT_ICE(Writer, diagnostics_) << "encountered non-zero resource group index (use "
                                               "BindingRemapper to fix)";
             return kInvalidBindingIndex;
         }
-        return bp.binding;
+        return bp->binding;
     };
 
     {
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 0a3009c..4a5c90b 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -867,14 +867,14 @@
             [&](const ast::BindingAttribute*) {
                 auto bp = sem->BindingPoint();
                 push_annot(spv::Op::OpDecorate, {Operand(var_id), U32Operand(SpvDecorationBinding),
-                                                 Operand(bp.binding)});
+                                                 Operand(bp->binding)});
                 return true;
             },
             [&](const ast::GroupAttribute*) {
                 auto bp = sem->BindingPoint();
                 push_annot(
                     spv::Op::OpDecorate,
-                    {Operand(var_id), U32Operand(SpvDecorationDescriptorSet), Operand(bp.group)});
+                    {Operand(var_id), U32Operand(SpvDecorationDescriptorSet), Operand(bp->group)});
                 return true;
             },
             [&](const ast::IdAttribute*) {