Import Tint changes from Dawn

Changes:
  - 6d4b35a14b589a1ac821b9ff4be2f13a8345ea80 ClampFragDepth: switch params to a struct. by Stephen White <senorblanco@chromium.org>
  - a2ca09a571f432c7ce1a068b356a28ee201568c2 Fix issue with break inside switch inside continuing. by dan sinclair <dsinclair@chromium.org>
  - b87d6571e934ca2a5c647312d98a1da56f20d0a0 [tint][utils] Add more comparison operators for Source::L... by Ben Clayton <bclayton@google.com>
  - 023f2ae8175ea5164b50fc7e1daef18687e178f3 [tint][core] Expose intrinsic::PrintOverload() by Ben Clayton <bclayton@google.com>
  - 82d62aa377a6d01c09c890138798f58b2aee5cf0 [tint][wgsl] Expand ast::Function source to include whole... by Ben Clayton <bclayton@google.com>
  - 7c8832ac22c07df7451b7c7774849d94d590ee4f [tint][sem] Store OverloadInfo on BuiltinFn by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 6d4b35a14b589a1ac821b9ff4be2f13a8345ea80
Change-Id: I592e4b0e397a9009f5d4b86b8ab78bfd8ca3403e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/174940
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/api/options/BUILD.bazel b/src/tint/api/options/BUILD.bazel
index 9a16e95..c7eba70 100644
--- a/src/tint/api/options/BUILD.bazel
+++ b/src/tint/api/options/BUILD.bazel
@@ -44,6 +44,7 @@
   hdrs = [
     "array_length_from_uniform.h",
     "binding_remapper.h",
+    "depth_range_offsets.h",
     "external_texture.h",
     "pixel_local.h",
     "texture_builtins_from_uniform.h",
diff --git a/src/tint/api/options/BUILD.cmake b/src/tint/api/options/BUILD.cmake
index 045323e..1f0fffa 100644
--- a/src/tint/api/options/BUILD.cmake
+++ b/src/tint/api/options/BUILD.cmake
@@ -41,6 +41,7 @@
 tint_add_target(tint_api_options lib
   api/options/array_length_from_uniform.h
   api/options/binding_remapper.h
+  api/options/depth_range_offsets.h
   api/options/external_texture.h
   api/options/options.cc
   api/options/pixel_local.h
diff --git a/src/tint/api/options/BUILD.gn b/src/tint/api/options/BUILD.gn
index f9f9b46..cc35c71 100644
--- a/src/tint/api/options/BUILD.gn
+++ b/src/tint/api/options/BUILD.gn
@@ -46,6 +46,7 @@
   sources = [
     "array_length_from_uniform.h",
     "binding_remapper.h",
+    "depth_range_offsets.h",
     "external_texture.h",
     "options.cc",
     "pixel_local.h",
diff --git a/src/tint/api/options/depth_range_offsets.h b/src/tint/api/options/depth_range_offsets.h
new file mode 100644
index 0000000..1abe2a5
--- /dev/null
+++ b/src/tint/api/options/depth_range_offsets.h
@@ -0,0 +1,49 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_API_OPTIONS_DEPTH_RANGE_OFFSETS_H_
+#define SRC_TINT_API_OPTIONS_DEPTH_RANGE_OFFSETS_H_
+
+#include <unordered_map>
+
+#include "src/tint/api/common/binding_point.h"
+
+namespace tint {
+
+/// Options used to specify the offsets of the min_depth and max_depth push constants.
+struct DepthRangeOffsets {
+    /// A map of old binding point to new binding point
+    uint32_t min = 0;
+    uint32_t max = 0;
+
+    /// Reflect the fields of this class so that it can be used by tint::ForeachField()
+    TINT_REFLECT(DepthRangeOffsets, min, max);
+};
+
+}  // namespace tint
+
+#endif  // SRC_TINT_API_OPTIONS_DEPTH_RANGE_OFFSETS_H_
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 1601a76..8db9a64 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -1089,8 +1089,7 @@
             offset += 4;
         }
         if (entry_point.frag_depth_used) {
-            gen_options.min_depth_offset = offset + 0;
-            gen_options.max_depth_offset = offset + 4;
+            gen_options.depth_range_offsets = {offset + 0, offset + 4};
             offset += 8;
         }
 
diff --git a/src/tint/lang/core/intrinsic/table.cc b/src/tint/lang/core/intrinsic/table.cc
index a8ee926..b81fde7 100644
--- a/src/tint/lang/core/intrinsic/table.cc
+++ b/src/tint/lang/core/intrinsic/table.cc
@@ -161,12 +161,6 @@
                  const NumberMatcherIndex* number_matcher_indices,
                  EvaluationStage earliest_eval_stage);
 
-// Prints the overload for emitting diagnostics
-void PrintOverload(StringStream& ss,
-                   Context& context,
-                   const OverloadInfo& overload,
-                   std::string_view intrinsic_name);
-
 // Prints the list of candidates for emitting diagnostics
 void PrintCandidates(StringStream& ss,
                      Context& context,
@@ -464,6 +458,57 @@
                       number_matcher_indices, earliest_eval_stage};
 }
 
+void PrintCandidates(StringStream& ss,
+                     Context& context,
+                     VectorRef<Candidate> candidates,
+                     std::string_view intrinsic_name) {
+    for (auto& candidate : candidates) {
+        ss << "  ";
+        PrintOverload(ss, context, *candidate.overload, intrinsic_name);
+        ss << std::endl;
+    }
+}
+
+std::string ErrAmbiguousOverload(Context& context,
+                                 std::string_view intrinsic_name,
+                                 VectorRef<const core::type::Type*> args,
+                                 TemplateState templates,
+                                 VectorRef<Candidate> candidates) {
+    StringStream ss;
+    ss << "ambiguous overload while attempting to match " << intrinsic_name;
+    for (size_t i = 0; i < std::numeric_limits<size_t>::max(); i++) {
+        if (auto* ty = templates.Type(i)) {
+            ss << ((i == 0) ? "<" : ", ") << ty->FriendlyName();
+        } else {
+            if (i > 0) {
+                ss << ">";
+            }
+            break;
+        }
+    }
+    ss << "(";
+    bool first = true;
+    for (auto* arg : args) {
+        if (!first) {
+            ss << ", ";
+        }
+        first = false;
+        ss << arg->FriendlyName();
+    }
+    ss << "):\n";
+    for (auto& candidate : candidates) {
+        if (candidate.score == 0) {
+            ss << "  ";
+            PrintOverload(ss, context, *candidate.overload, intrinsic_name);
+            ss << std::endl;
+        }
+    }
+    TINT_ICE() << ss.str();
+    return ss.str();
+}
+
+}  // namespace
+
 void PrintOverload(StringStream& ss,
                    Context& context,
                    const OverloadInfo& overload,
@@ -545,57 +590,6 @@
     }
 }
 
-void PrintCandidates(StringStream& ss,
-                     Context& context,
-                     VectorRef<Candidate> candidates,
-                     std::string_view intrinsic_name) {
-    for (auto& candidate : candidates) {
-        ss << "  ";
-        PrintOverload(ss, context, *candidate.overload, intrinsic_name);
-        ss << std::endl;
-    }
-}
-
-std::string ErrAmbiguousOverload(Context& context,
-                                 std::string_view intrinsic_name,
-                                 VectorRef<const core::type::Type*> args,
-                                 TemplateState templates,
-                                 VectorRef<Candidate> candidates) {
-    StringStream ss;
-    ss << "ambiguous overload while attempting to match " << intrinsic_name;
-    for (size_t i = 0; i < std::numeric_limits<size_t>::max(); i++) {
-        if (auto* ty = templates.Type(i)) {
-            ss << ((i == 0) ? "<" : ", ") << ty->FriendlyName();
-        } else {
-            if (i > 0) {
-                ss << ">";
-            }
-            break;
-        }
-    }
-    ss << "(";
-    bool first = true;
-    for (auto* arg : args) {
-        if (!first) {
-            ss << ", ";
-        }
-        first = false;
-        ss << arg->FriendlyName();
-    }
-    ss << "):\n";
-    for (auto& candidate : candidates) {
-        if (candidate.score == 0) {
-            ss << "  ";
-            PrintOverload(ss, context, *candidate.overload, intrinsic_name);
-            ss << std::endl;
-        }
-    }
-    TINT_ICE() << ss.str();
-    return ss.str();
-}
-
-}  // namespace
-
 Result<Overload, std::string> LookupFn(Context& context,
                                        std::string_view intrinsic_name,
                                        size_t function_id,
diff --git a/src/tint/lang/core/intrinsic/table.h b/src/tint/lang/core/intrinsic/table.h
index c1cbfcc..8228c1f 100644
--- a/src/tint/lang/core/intrinsic/table.h
+++ b/src/tint/lang/core/intrinsic/table.h
@@ -106,6 +106,12 @@
     SymbolTable& symbols;
 };
 
+// Prints the overload for emitting diagnostics
+void PrintOverload(StringStream& ss,
+                   Context& context,
+                   const OverloadInfo& overload,
+                   std::string_view intrinsic_name);
+
 /// Lookup looks for the builtin overload with the given signature, raising an error diagnostic
 /// if the builtin was not found.
 /// @param context the intrinsic context
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
index 764b779..5020b17 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -254,8 +254,8 @@
 
     data.Add<ast::transform::OffsetFirstIndex::Config>(std::nullopt, options.first_instance_offset);
 
-    data.Add<ast::transform::ClampFragDepth::Config>(options.min_depth_offset,
-                                                     options.max_depth_offset);
+    data.Add<ast::transform::ClampFragDepth::Config>(options.depth_range_offsets);
+
     SanitizedResult result;
     ast::transform::DataMap outputs;
     result.program = manager.Run(in, data, outputs);
diff --git a/src/tint/lang/glsl/writer/common/options.h b/src/tint/lang/glsl/writer/common/options.h
index 62cd80c..19aa11d 100644
--- a/src/tint/lang/glsl/writer/common/options.h
+++ b/src/tint/lang/glsl/writer/common/options.h
@@ -33,6 +33,7 @@
 #include <unordered_map>
 
 #include "src/tint/api/options/binding_remapper.h"
+#include "src/tint/api/options/depth_range_offsets.h"
 #include "src/tint/api/options/external_texture.h"
 #include "src/tint/api/options/texture_builtins_from_uniform.h"
 #include "src/tint/lang/glsl/writer/common/version.h"
@@ -82,11 +83,8 @@
     /// Offset of the firstInstance push constant.
     std::optional<int32_t> first_instance_offset;
 
-    /// Offset of the minDepth push constant.
-    std::optional<uint32_t> min_depth_offset;
-
-    /// Offset of the maxDepth push constant.
-    std::optional<uint32_t> max_depth_offset;
+    /// Offsets of the minDepth and maxDepth push constants.
+    std::optional<DepthRangeOffsets> depth_range_offsets;
 
     /// Options used to map WGSL textureNumLevels/textureNumSamples builtins to internal uniform
     /// buffer values. If not specified, emits corresponding GLSL builtins
@@ -104,8 +102,7 @@
                  binding_remapper_options,
                  external_texture_options,
                  first_instance_offset,
-                 min_depth_offset,
-                 max_depth_offset,
+                 depth_range_offsets,
                  texture_builtins_from_uniform);
 };
 
diff --git a/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc b/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
index c782db5..1d75ad5 100644
--- a/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
@@ -66,7 +66,7 @@
 
     if (options.clamp_frag_depth) {
         manager.Add<ast::transform::ClampFragDepth>();
-        data.Add<ast::transform::ClampFragDepth::Config>(0, 4);
+        data.Add<ast::transform::ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     }
 
     manager.Add<ast::transform::DisableUniformityAnalysis>();
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc
index 2b5e6a2..66bf672 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc
@@ -67,7 +67,7 @@
     /// @returns the new program or SkipTransform if the transform is not required
     Transform::ApplyResult Run(const DataMap& inputs) {
         const Config* cfg = inputs.Get<Config>();
-        if (!cfg || !cfg->min_depth_offset.has_value() || !cfg->max_depth_offset.has_value()) {
+        if (!cfg || !cfg->offsets.has_value()) {
             return SkipTransform;
         }
 
@@ -87,8 +87,8 @@
         //       return clamp(v, push_constants.min, push_constants.max_depth);
         //   }
 
-        push_constant_helper.InsertMember("min_depth", b.ty.f32(), *cfg->min_depth_offset);
-        push_constant_helper.InsertMember("max_depth", b.ty.f32(), *cfg->max_depth_offset);
+        push_constant_helper.InsertMember("min_depth", b.ty.f32(), cfg->offsets->min);
+        push_constant_helper.InsertMember("max_depth", b.ty.f32(), cfg->offsets->max);
 
         Symbol buffer_name = push_constant_helper.Run();
 
@@ -221,9 +221,7 @@
     return State{src}.Run(inputs);
 }
 
-ClampFragDepth::Config::Config(std::optional<uint32_t> min_depth_off,
-                               std::optional<uint32_t> max_depth_off)
-    : min_depth_offset(min_depth_off), max_depth_offset(max_depth_off) {}
+ClampFragDepth::Config::Config(std::optional<tint::DepthRangeOffsets> off) : offsets(off) {}
 
 ClampFragDepth::Config::~Config() = default;
 
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h
index 599b91c..03972a0 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h
@@ -28,6 +28,7 @@
 #ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_CLAMP_FRAG_DEPTH_H_
 #define SRC_TINT_LANG_WGSL_AST_TRANSFORM_CLAMP_FRAG_DEPTH_H_
 
+#include "src/tint/api/options/depth_range_offsets.h"
 #include "src/tint/lang/wgsl/ast/transform/transform.h"
 
 namespace tint::ast::transform {
@@ -72,18 +73,14 @@
     /// Transform configuration options
     struct Config final : public Castable<Config, ast::transform::Data> {
         /// Constructor
-        /// @param min_depth_off Offset of the minDepth push constant
-        /// @param max_depth_off Offset of the maxDepth push constant
-        Config(std::optional<uint32_t> min_depth_off, std::optional<uint32_t> max_depth_off);
+        /// @param off Offsets of the min_depth and max_depth push constants
+        explicit Config(std::optional<tint::DepthRangeOffsets> off);
 
         /// Destructor
         ~Config() override;
 
-        /// Offset of the min_depth push constant
-        std::optional<uint32_t> min_depth_offset;
-
-        /// Offset of the min_depth push constant
-        std::optional<uint32_t> max_depth_offset;
+        /// Offsets of the min_depth and max_depth push constants
+        std::optional<tint::DepthRangeOffsets> offsets;
     };
 
     /// @copydoc ast::transform::Transform::Apply
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc
index 97f1f31..027c19d 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc
@@ -50,19 +50,6 @@
     EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
 }
 
-TEST_F(ClampFragDepthTest, ShouldRunNoMin) {
-    auto* src = R"(
-        @fragment fn main() -> @builtin(frag_depth) f32 {
-            return 0.0;
-        }
-    )";
-
-    DataMap config;
-    config.Add<ClampFragDepth::Config>(std::nullopt, 4);
-
-    EXPECT_FALSE(ShouldRun<ClampFragDepth>(src, config));
-}
-
 TEST_F(ClampFragDepthTest, ShouldRunNoMinNoMax) {
     auto* src = R"(
         @fragment fn main() -> @builtin(frag_depth) f32 {
@@ -71,7 +58,7 @@
     )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, std::nullopt);
+    config.Add<ClampFragDepth::Config>(std::nullopt);
 
     EXPECT_FALSE(ShouldRun<ClampFragDepth>(src, config));
 }
@@ -84,7 +71,7 @@
     )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
 
     EXPECT_TRUE(ShouldRun<ClampFragDepth>(src, config));
 }
@@ -132,7 +119,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(4, 8);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{4, 8});
 
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
@@ -146,7 +133,7 @@
     )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
 
     EXPECT_TRUE(ShouldRun<ClampFragDepth>(src, config));
 }
@@ -164,7 +151,7 @@
     )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
 
     EXPECT_TRUE(ShouldRun<ClampFragDepth>(src, config));
 }
@@ -199,7 +186,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
@@ -240,7 +227,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
@@ -283,7 +270,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
@@ -331,7 +318,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
@@ -409,7 +396,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
@@ -469,7 +456,7 @@
 )";
 
     DataMap config;
-    config.Add<ClampFragDepth::Config>(0, 4);
+    config.Add<ClampFragDepth::Config>(tint::DepthRangeOffsets{0, 4});
     auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
diff --git a/src/tint/lang/wgsl/reader/parser/function_decl_test.cc b/src/tint/lang/wgsl/reader/parser/function_decl_test.cc
index 99aa00f..212e5b9 100644
--- a/src/tint/lang/wgsl/reader/parser/function_decl_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/function_decl_test.cc
@@ -45,6 +45,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{1, 1}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{1, 38}));
+
     EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
 
@@ -88,6 +91,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{1, 1}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{1, 114}));
+
     EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get(function_ident));
     EXPECT_EQ(f->return_type, nullptr);
 
@@ -114,6 +120,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{1, 26}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{1, 47}));
+
     EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
     ASSERT_EQ(f->params.Length(), 0u);
@@ -158,6 +167,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{3, 1}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{3, 22}));
+
     EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
     ASSERT_EQ(f->params.Length(), 0u);
@@ -206,6 +218,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{4, 1}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{4, 22}));
+
     EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     EXPECT_EQ(f->return_type, nullptr);
     ASSERT_EQ(f->params.Length(), 0u);
@@ -251,6 +266,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{1, 1}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{1, 46}));
+
     EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
     ASSERT_NE(f->return_type, nullptr);
 
@@ -287,6 +305,9 @@
     EXPECT_TRUE(f.matched);
     ASSERT_NE(f.value, nullptr);
 
+    EXPECT_EQ(f->source.range.begin, (Source::Location{1, 11}));
+    EXPECT_EQ(f->source.range.end, (Source::Location{1, 32}));
+
     auto& attributes = f->attributes;
     ASSERT_EQ(attributes.Length(), 1u);
     ASSERT_TRUE(attributes[0]->Is<ast::MustUseAttribute>());
diff --git a/src/tint/lang/wgsl/reader/parser/parser.cc b/src/tint/lang/wgsl/reader/parser/parser.cc
index 3a05f2d..0781f66 100644
--- a/src/tint/lang/wgsl/reader/parser/parser.cc
+++ b/src/tint/lang/wgsl/reader/parser/parser.cc
@@ -1060,6 +1060,8 @@
 // function_decl
 //   : function_header compound_statement
 Maybe<const ast::Function*> Parser::function_decl(AttributeList& attrs) {
+    MultiTokenSource source(this);
+
     auto header = function_header();
     if (header.errored) {
         if (sync_to(Token::Type::kBraceLeft, /* consume: */ false)) {
@@ -1090,8 +1092,8 @@
 
     TINT_DEFER(attrs.Clear());
 
-    return builder_.Func(header->source, header->name, header->params, header->return_type,
-                         body.value, std::move(attrs), header->return_type_attributes);
+    return builder_.Func(source, header->name, header->params, header->return_type, body.value,
+                         std::move(attrs), header->return_type_attributes);
 }
 
 // function_header
diff --git a/src/tint/lang/wgsl/resolver/resolver.cc b/src/tint/lang/wgsl/resolver/resolver.cc
index 76b9ff9..29d313d 100644
--- a/src/tint/lang/wgsl/resolver/resolver.cc
+++ b/src/tint/lang/wgsl/resolver/resolver.cc
@@ -2415,9 +2415,8 @@
         }
         auto eval_stage = overload->const_eval_fn ? core::EvaluationStage::kConstant
                                                   : core::EvaluationStage::kRuntime;
-        return b.create<sem::BuiltinFn>(
-            fn, overload->return_type, std::move(params), eval_stage, supported_stages,
-            flags.Contains(OverloadFlag::kIsDeprecated), flags.Contains(OverloadFlag::kMustUse));
+        return b.create<sem::BuiltinFn>(fn, overload->return_type, std::move(params), eval_stage,
+                                        supported_stages, *overload->info);
     });
 
     if (fn == wgsl::BuiltinFn::kTintMaterialize) {
diff --git a/src/tint/lang/wgsl/resolver/validation_test.cc b/src/tint/lang/wgsl/resolver/validation_test.cc
index 6bc6fc2..ba5da4a 100644
--- a/src/tint/lang/wgsl/resolver/validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/validation_test.cc
@@ -994,6 +994,22 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverValidationTest, Stmt_BreakInSwitchInContinuing) {
+    // loop {
+    //   continuing {
+    //     switch(1) {
+    //       default:
+    //         break;
+    //     }
+    //   }
+    // }
+
+    auto* cont = Block(Switch(1_i, DefaultCase(Block(Break()))));
+
+    WrapInFunction(Loop(Block(Break()), cont));
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueInContinuing) {
     auto* cont = Block(                           // continuing {
         If(true, Block(                           //   if(true) {
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index 8e54a74..3cf40c0 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -266,11 +266,15 @@
 }
 
 const ast::Statement* Validator::ClosestContinuing(bool stop_at_loop,
+                                                   bool stop_at_switch,
                                                    sem::Statement* current_statement) const {
     for (const auto* s = current_statement; s != nullptr; s = s->Parent()) {
         if (stop_at_loop && s->Is<sem::LoopStatement>()) {
             break;
         }
+        if (stop_at_switch && s->Is<sem::SwitchStatement>()) {
+            break;
+        }
         if (s->Is<sem::LoopContinuingBlockStatement>()) {
             return s->Declaration();
         }
@@ -1583,7 +1587,8 @@
         AddError("break statement must be in a loop or switch case", stmt->Declaration()->source);
         return false;
     }
-    if (ClosestContinuing(/*stop_at_loop*/ true, current_statement) != nullptr) {
+    if (ClosestContinuing(/*stop_at_loop*/ true, /* stop_at_switch */ true, current_statement) !=
+        nullptr) {
         AddError(
             "`break` must not be used to exit from a continuing block. Use `break-if` instead.",
             stmt->Declaration()->source);
@@ -1594,7 +1599,8 @@
 
 bool Validator::ContinueStatement(const sem::Statement* stmt,
                                   sem::Statement* current_statement) const {
-    if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
+    if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, /* stop_at_switch */ false,
+                                             current_statement)) {
         AddError("continuing blocks must not contain a continue statement",
                  stmt->Declaration()->source);
         if (continuing != stmt->Declaration() && continuing != stmt->Parent()->Declaration()) {
@@ -2463,7 +2469,8 @@
     }
 
     auto* sem = sem_.Get(ret);
-    if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
+    if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, /* stop_at_switch */ false,
+                                             current_statement)) {
         AddError("continuing blocks must not contain a return statement", ret->source);
         if (continuing != sem->Declaration() && continuing != sem->Parent()->Declaration()) {
             AddNote("see continuing block here", continuing->source);
diff --git a/src/tint/lang/wgsl/resolver/validator.h b/src/tint/lang/wgsl/resolver/validator.h
index 92ccf7c..fc42824 100644
--- a/src/tint/lang/wgsl/resolver/validator.h
+++ b/src/tint/lang/wgsl/resolver/validator.h
@@ -579,8 +579,11 @@
     /// (transitively) owns the current statement.
     /// @param stop_at_loop if true then the function will return nullptr if a
     /// loop or for-loop was found before the continuing.
+    /// @param stop_at_switch if true then the function will return nullptr if a switch was found
+    /// before continuing.
     /// @param current_statement the current statement being resolved
     const ast::Statement* ClosestContinuing(bool stop_at_loop,
+                                            bool stop_at_switch,
                                             sem::Statement* current_statement) const;
 
     /// Returns a human-readable string representation of the vector type name
diff --git a/src/tint/lang/wgsl/sem/BUILD.bazel b/src/tint/lang/wgsl/sem/BUILD.bazel
index 75399ed..37b8323 100644
--- a/src/tint/lang/wgsl/sem/BUILD.bazel
+++ b/src/tint/lang/wgsl/sem/BUILD.bazel
@@ -113,6 +113,7 @@
     "//src/tint/api/common",
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
+    "//src/tint/lang/core/intrinsic",
     "//src/tint/lang/core/type",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
diff --git a/src/tint/lang/wgsl/sem/BUILD.cmake b/src/tint/lang/wgsl/sem/BUILD.cmake
index eba7340..6120b90 100644
--- a/src/tint/lang/wgsl/sem/BUILD.cmake
+++ b/src/tint/lang/wgsl/sem/BUILD.cmake
@@ -112,6 +112,7 @@
   tint_api_common
   tint_lang_core
   tint_lang_core_constant
+  tint_lang_core_intrinsic
   tint_lang_core_type
   tint_lang_wgsl
   tint_lang_wgsl_ast
diff --git a/src/tint/lang/wgsl/sem/BUILD.gn b/src/tint/lang/wgsl/sem/BUILD.gn
index 715f3a6..e6b2b06 100644
--- a/src/tint/lang/wgsl/sem/BUILD.gn
+++ b/src/tint/lang/wgsl/sem/BUILD.gn
@@ -116,6 +116,7 @@
     "${tint_src_dir}/api/common",
     "${tint_src_dir}/lang/core",
     "${tint_src_dir}/lang/core/constant",
+    "${tint_src_dir}/lang/core/intrinsic",
     "${tint_src_dir}/lang/core/type",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
diff --git a/src/tint/lang/wgsl/sem/builtin_fn.cc b/src/tint/lang/wgsl/sem/builtin_fn.cc
index 0e48dfc..b0218eb 100644
--- a/src/tint/lang/wgsl/sem/builtin_fn.cc
+++ b/src/tint/lang/wgsl/sem/builtin_fn.cc
@@ -33,6 +33,7 @@
 #include <utility>
 #include <vector>
 
+#include "src/tint/lang/core/intrinsic/table.h"
 #include "src/tint/utils/containers/transform.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinFn);
@@ -48,15 +49,21 @@
                      VectorRef<Parameter*> parameters,
                      core::EvaluationStage eval_stage,
                      PipelineStageSet supported_stages,
-                     bool is_deprecated,
-                     bool must_use)
-    : Base(return_type, std::move(parameters), eval_stage, must_use),
+                     const core::intrinsic::OverloadInfo& overload)
+    : Base(return_type,
+           std::move(parameters),
+           eval_stage,
+           overload.flags.Contains(core::intrinsic::OverloadFlag::kMustUse)),
       fn_(type),
       supported_stages_(supported_stages),
-      is_deprecated_(is_deprecated) {}
+      overload_(overload) {}
 
 BuiltinFn::~BuiltinFn() = default;
 
+bool BuiltinFn::IsDeprecated() const {
+    return overload_.flags.Contains(core::intrinsic::OverloadFlag::kIsDeprecated);
+}
+
 bool BuiltinFn::IsCoarseDerivative() const {
     return wgsl::IsCoarseDerivative(fn_);
 }
diff --git a/src/tint/lang/wgsl/sem/builtin_fn.h b/src/tint/lang/wgsl/sem/builtin_fn.h
index bcac245..18684cc 100644
--- a/src/tint/lang/wgsl/sem/builtin_fn.h
+++ b/src/tint/lang/wgsl/sem/builtin_fn.h
@@ -38,6 +38,11 @@
 #include "src/tint/lang/wgsl/sem/pipeline_stage_set.h"
 #include "src/tint/utils/math/hash.h"
 
+// Forward declarations
+namespace tint::core::intrinsic {
+struct OverloadInfo;
+}
+
 namespace tint::sem {
 
 /// BuiltinFn holds the semantic information for a builtin function.
@@ -49,15 +54,13 @@
     /// @param parameters the parameters for the builtin overload
     /// @param eval_stage the earliest evaluation stage for a call to the builtin
     /// @param supported_stages the pipeline stages that this builtin can be used in
-    /// @param is_deprecated true if the particular overload is considered deprecated
-    /// @param must_use true if the builtin was annotated with `@must_use`
+    /// @param overload the builtin table overload
     BuiltinFn(wgsl::BuiltinFn type,
               const core::type::Type* return_type,
               VectorRef<Parameter*> parameters,
               core::EvaluationStage eval_stage,
               PipelineStageSet supported_stages,
-              bool is_deprecated,
-              bool must_use);
+              const core::intrinsic::OverloadInfo& overload);
 
     /// Destructor
     ~BuiltinFn() override;
@@ -69,7 +72,7 @@
     PipelineStageSet SupportedStages() const { return supported_stages_; }
 
     /// @return true if the builtin overload is considered deprecated
-    bool IsDeprecated() const { return is_deprecated_; }
+    bool IsDeprecated() const;
 
     /// @returns the name of the builtin function type. The spelling, including
     /// case, matches the name in the WGSL spec.
@@ -122,6 +125,9 @@
     /// wgsl::LanguageFeature::kUndefined if no language feature is required.
     wgsl::LanguageFeature RequiredLanguageFeature() const;
 
+    /// @returns the builtin table overload info
+    const core::intrinsic::OverloadInfo& Overload() const { return overload_; }
+
     /// @return the hash code for this object
     tint::HashCode HashCode() const {
         return Hash(Fn(), SupportedStages(), ReturnType(), Parameters(), IsDeprecated());
@@ -130,7 +136,7 @@
   private:
     const wgsl::BuiltinFn fn_;
     const PipelineStageSet supported_stages_;
-    const bool is_deprecated_;
+    const core::intrinsic::OverloadInfo& overload_;
 };
 
 /// Constant value used by the degrees() builtin
diff --git a/src/tint/utils/diagnostic/source.h b/src/tint/utils/diagnostic/source.h
index 8400921..afef58c 100644
--- a/src/tint/utils/diagnostic/source.h
+++ b/src/tint/utils/diagnostic/source.h
@@ -97,10 +97,31 @@
         /// Returns true if `this` location is lexicographically less than `rhs`
         /// @param rhs location to compare against
         /// @returns true if `this` < `rhs`
-        inline bool operator<(const Source::Location& rhs) {
+        inline bool operator<(const Source::Location& rhs) const {
             return std::tie(line, column) < std::tie(rhs.line, rhs.column);
         }
 
+        /// Returns true if `this` location is lexicographically less than or equal to `rhs`
+        /// @param rhs location to compare against
+        /// @returns true if `this` <= `rhs`
+        inline bool operator<=(const Source::Location& rhs) const {
+            return std::tie(line, column) <= std::tie(rhs.line, rhs.column);
+        }
+
+        /// Returns true if `this` location is lexicographically greater than `rhs`
+        /// @param rhs location to compare against
+        /// @returns true if `this` > `rhs`
+        inline bool operator>(const Source::Location& rhs) const {
+            return std::tie(line, column) > std::tie(rhs.line, rhs.column);
+        }
+
+        /// Returns true if `this` location is lexicographically greater than or equal to `rhs`
+        /// @param rhs location to compare against
+        /// @returns true if `this` >= `rhs`
+        inline bool operator>=(const Source::Location& rhs) const {
+            return std::tie(line, column) >= std::tie(rhs.line, rhs.column);
+        }
+
         /// Returns true of `this` location is equal to `rhs`
         /// @param rhs location to compare against
         /// @returns true if `this` == `rhs`