[tint] Add TINT_ASSERT_ALL_FIELDS_REFLECTED() macro

Performs a compile-time assertion that all the fields of the given class have been reflected with TINT_REFLECT().

Change-Id: I4813486037715801a8b011c23f38e31e367c894c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/172600
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/tint/api/common/binding_point.h b/src/tint/api/common/binding_point.h
index 64d61f6..b602c81 100644
--- a/src/tint/api/common/binding_point.h
+++ b/src/tint/api/common/binding_point.h
@@ -46,7 +46,7 @@
     uint32_t binding = 0;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(group, binding);
+    TINT_REFLECT(BindingPoint, group, binding);
 
     /// Equality operator
     /// @param rhs the BindingPoint to compare against
@@ -74,6 +74,9 @@
     }
 };
 
+/// Ensure that all the fields of BindingPoint are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(BindingPoint);
+
 /// Prints the BindingPoint @p bp to @p o
 /// @param o the stream to write to
 /// @param bp the BindingPoint
diff --git a/src/tint/api/common/override_id.h b/src/tint/api/common/override_id.h
index 70abc33..f7a6837 100644
--- a/src/tint/api/common/override_id.h
+++ b/src/tint/api/common/override_id.h
@@ -41,9 +41,12 @@
     uint16_t value = 0;
 
     /// Reflect the fields of this struct so that it can be used by tint::ForeachField()
-    TINT_REFLECT(value);
+    TINT_REFLECT(OverrideId, value);
 };
 
+/// Ensure that all the fields of OverrideId are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(OverrideId);
+
 /// Equality operator for OverrideId
 /// @param lhs the OverrideId on the left of the '=' operator
 /// @param rhs the OverrideId on the right of the '=' operator
diff --git a/src/tint/api/options/array_length_from_uniform.h b/src/tint/api/options/array_length_from_uniform.h
index ba7094a..f46825f 100644
--- a/src/tint/api/options/array_length_from_uniform.h
+++ b/src/tint/api/options/array_length_from_uniform.h
@@ -45,9 +45,12 @@
     std::unordered_map<BindingPoint, uint32_t> bindpoint_to_size_index;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(ubo_binding, bindpoint_to_size_index);
+    TINT_REFLECT(ArrayLengthFromUniformOptions, ubo_binding, bindpoint_to_size_index);
 };
 
+/// Ensure that all the fields of ArrayLengthFromUniformOptions are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(ArrayLengthFromUniformOptions);
+
 }  // namespace tint
 
 #endif  // SRC_TINT_API_OPTIONS_ARRAY_LENGTH_FROM_UNIFORM_H_
diff --git a/src/tint/api/options/binding_remapper.h b/src/tint/api/options/binding_remapper.h
index 493c854..4e1e4f8 100644
--- a/src/tint/api/options/binding_remapper.h
+++ b/src/tint/api/options/binding_remapper.h
@@ -43,9 +43,12 @@
     BindingPoints binding_points;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(binding_points);
+    TINT_REFLECT(BindingRemapperOptions, binding_points);
 };
 
+/// Ensure that all the fields of BindingRemapperOptions are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(BindingRemapperOptions);
+
 }  // namespace tint
 
 #endif  // SRC_TINT_API_OPTIONS_BINDING_REMAPPER_H_
diff --git a/src/tint/api/options/external_texture.h b/src/tint/api/options/external_texture.h
index ff01aec..76e7898 100644
--- a/src/tint/api/options/external_texture.h
+++ b/src/tint/api/options/external_texture.h
@@ -47,7 +47,7 @@
         BindingPoint params;
 
         /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-        TINT_REFLECT(plane_1, params);
+        TINT_REFLECT(BindingPoints, plane_1, params);
     };
 
     /// BindingsMap is a map where the key is the binding location of a
@@ -59,9 +59,15 @@
     BindingsMap bindings_map;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(bindings_map);
+    TINT_REFLECT(ExternalTextureOptions, bindings_map);
 };
 
+/// Ensure that all the fields of ExternalTextureOptions::BindingPoints are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(ExternalTextureOptions::BindingPoints);
+
+/// Ensure that all the fields of ExternalTextureOptions are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(ExternalTextureOptions);
+
 }  // namespace tint
 
 #endif  // SRC_TINT_API_OPTIONS_EXTERNAL_TEXTURE_H_
diff --git a/src/tint/api/options/pixel_local.h b/src/tint/api/options/pixel_local.h
index 1b4e200..64022c3 100644
--- a/src/tint/api/options/pixel_local.h
+++ b/src/tint/api/options/pixel_local.h
@@ -54,9 +54,12 @@
     uint32_t pixel_local_group_index = 0;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(attachments, attachment_formats, pixel_local_group_index);
+    TINT_REFLECT(PixelLocalOptions, attachments, attachment_formats, pixel_local_group_index);
 };
 
+/// Ensure that all the fields of PixelLocalOptions are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(PixelLocalOptions);
+
 /// Reflect valid value ranges for the PixelLocalOptions::TexelFormat enum.
 TINT_REFLECT_ENUM_RANGE(PixelLocalOptions::TexelFormat, kR32Sint, kR32Float);
 
diff --git a/src/tint/api/options/texture_builtins_from_uniform.h b/src/tint/api/options/texture_builtins_from_uniform.h
index ac107e5..b929c6b 100644
--- a/src/tint/api/options/texture_builtins_from_uniform.h
+++ b/src/tint/api/options/texture_builtins_from_uniform.h
@@ -47,9 +47,12 @@
     std::vector<BindingPoint> ubo_bindingpoint_ordering = {};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(ubo_binding, ubo_bindingpoint_ordering);
+    TINT_REFLECT(TextureBuiltinsFromUniformOptions, ubo_binding, ubo_bindingpoint_ordering);
 };
 
+/// Ensure that all the fields of TextureBuiltinsFromUniformOptions are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(TextureBuiltinsFromUniformOptions);
+
 }  // namespace tint
 
 #endif  // SRC_TINT_API_OPTIONS_TEXTURE_BUILTINS_FROM_UNIFORM_H_
diff --git a/src/tint/lang/core/BUILD.bazel b/src/tint/lang/core/BUILD.bazel
index ade8daa..6515589 100644
--- a/src/tint/lang/core/BUILD.bazel
+++ b/src/tint/lang/core/BUILD.bazel
@@ -145,6 +145,7 @@
   deps = [
     "//src/tint/lang/core",
     "//src/tint/utils/macros",
+    "//src/tint/utils/math",
     "//src/tint/utils/reflection",
     "//src/tint/utils/traits",
     "@benchmark",
diff --git a/src/tint/lang/core/BUILD.cmake b/src/tint/lang/core/BUILD.cmake
index ea40bb5..d9786fe 100644
--- a/src/tint/lang/core/BUILD.cmake
+++ b/src/tint/lang/core/BUILD.cmake
@@ -152,6 +152,7 @@
 tint_target_add_dependencies(tint_lang_core_bench bench
   tint_lang_core
   tint_utils_macros
+  tint_utils_math
   tint_utils_reflection
   tint_utils_traits
 )
diff --git a/src/tint/lang/core/BUILD.gn b/src/tint/lang/core/BUILD.gn
index b1186f8..cd834ac 100644
--- a/src/tint/lang/core/BUILD.gn
+++ b/src/tint/lang/core/BUILD.gn
@@ -144,6 +144,7 @@
       "${tint_src_dir}:google_benchmark",
       "${tint_src_dir}/lang/core",
       "${tint_src_dir}/utils/macros",
+      "${tint_src_dir}/utils/math",
       "${tint_src_dir}/utils/reflection",
       "${tint_src_dir}/utils/traits",
     ]
diff --git a/src/tint/lang/glsl/writer/common/options.h b/src/tint/lang/glsl/writer/common/options.h
index 1f5298f..31e1244 100644
--- a/src/tint/lang/glsl/writer/common/options.h
+++ b/src/tint/lang/glsl/writer/common/options.h
@@ -88,7 +88,8 @@
     TextureBuiltinsFromUniformOptions texture_builtins_from_uniform = {};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(disable_robustness,
+    TINT_REFLECT(Options,
+                 disable_robustness,
                  disable_workgroup_init,
                  disable_polyfill_integer_div_mod,
                  version,
@@ -96,9 +97,13 @@
                  placeholder_binding_point,
                  binding_remapper_options,
                  external_texture_options,
+                 first_instance_offset,
                  texture_builtins_from_uniform);
 };
 
+/// Ensure that all the fields of Options are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Options);
+
 }  // namespace tint::glsl::writer
 
 #endif  // SRC_TINT_LANG_GLSL_WRITER_COMMON_OPTIONS_H_
diff --git a/src/tint/lang/glsl/writer/common/version.h b/src/tint/lang/glsl/writer/common/version.h
index 9c3463a..7eb682e 100644
--- a/src/tint/lang/glsl/writer/common/version.h
+++ b/src/tint/lang/glsl/writer/common/version.h
@@ -68,9 +68,12 @@
     uint32_t minor_version = 1;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(standard, major_version, minor_version);
+    TINT_REFLECT(Version, standard, major_version, minor_version);
 };
 
+/// Ensure that all the fields of Version are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Version);
+
 }  // namespace tint::glsl::writer
 
 namespace tint {
diff --git a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h b/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h
index ff2830b..d8a284a 100644
--- a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h
+++ b/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h
@@ -124,9 +124,12 @@
         std::bitset<30> interstage_locations;
 
         /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-        TINT_REFLECT(interstage_variables);
+        TINT_REFLECT(Config, interstage_locations);
     };
 
+    /// Ensure that all the fields of Config are reflected.
+    TINT_ASSERT_ALL_FIELDS_REFLECTED(Config);
+
     /// Constructor using a the configuration provided in the input Data
     TruncateInterstageVariables();
 
diff --git a/src/tint/lang/hlsl/writer/common/options.h b/src/tint/lang/hlsl/writer/common/options.h
index 50cf766..b8d9a75 100644
--- a/src/tint/lang/hlsl/writer/common/options.h
+++ b/src/tint/lang/hlsl/writer/common/options.h
@@ -108,7 +108,8 @@
     PixelLocalOptions pixel_local_options = {};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(disable_robustness,
+    TINT_REFLECT(Options,
+                 disable_robustness,
                  disable_workgroup_init,
                  truncate_interstage_variables,
                  polyfill_reflect_vec2_f32,
@@ -125,6 +126,9 @@
                  pixel_local_options);
 };
 
+/// Ensure that all the fields of Options are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Options);
+
 }  // namespace tint::hlsl::writer
 
 #endif  // SRC_TINT_LANG_HLSL_WRITER_COMMON_OPTIONS_H_
diff --git a/src/tint/lang/msl/writer/common/options.h b/src/tint/lang/msl/writer/common/options.h
index 1004293..3b359ab 100644
--- a/src/tint/lang/msl/writer/common/options.h
+++ b/src/tint/lang/msl/writer/common/options.h
@@ -53,8 +53,12 @@
     inline bool operator!=(const BindingInfo& rhs) const { return !(*this == rhs); }
 
     /// Reflect the fields of this class so taht it can be used by tint::ForeachField()
-    TINT_REFLECT(binding);
+    TINT_REFLECT(BindingInfo, binding);
 };
+
+/// Ensure that all the fields of BindingInfo are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(BindingInfo);
+
 using Uniform = BindingInfo;
 using Storage = BindingInfo;
 using Texture = BindingInfo;
@@ -71,9 +75,12 @@
     BindingInfo plane1{};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(metadata, plane0, plane1);
+    TINT_REFLECT(ExternalTexture, metadata, plane0, plane1);
 };
 
+/// Ensure that all the fields of ExternalTexture are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(ExternalTexture);
+
 }  // namespace binding
 
 /// Maps the WGSL binding point to the SPIR-V group,binding for uniforms
@@ -105,9 +112,12 @@
     ExternalTextureBindings external_texture{};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(uniform, storage, texture, storage_texture, sampler, external_texture);
+    TINT_REFLECT(Bindings, uniform, storage, texture, storage_texture, sampler, external_texture);
 };
 
+/// Ensure that all the fields of Bindings are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Bindings);
+
 /// Configuration options used for generating MSL.
 struct Options {
     /// Constructor
@@ -152,7 +162,8 @@
     Bindings bindings;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(disable_robustness,
+    TINT_REFLECT(Options,
+                 disable_robustness,
                  disable_workgroup_init,
                  emit_vertex_point_size,
                  disable_polyfill_integer_div_mod,
@@ -163,6 +174,9 @@
                  bindings);
 };
 
+/// Ensure that all the fields of Options are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Options);
+
 }  // namespace tint::msl::writer
 
 #endif  // SRC_TINT_LANG_MSL_WRITER_COMMON_OPTIONS_H_
diff --git a/src/tint/lang/spirv/writer/common/options.h b/src/tint/lang/spirv/writer/common/options.h
index 494369c..4fcbafe 100644
--- a/src/tint/lang/spirv/writer/common/options.h
+++ b/src/tint/lang/spirv/writer/common/options.h
@@ -55,8 +55,12 @@
     inline bool operator!=(const BindingInfo& rhs) const { return !(*this == rhs); }
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(group, binding);
+    TINT_REFLECT(BindingInfo, group, binding);
 };
+
+/// Ensure that all the fields of BindingInfo are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(BindingInfo);
+
 using Uniform = BindingInfo;
 using Storage = BindingInfo;
 using Texture = BindingInfo;
@@ -73,9 +77,12 @@
     BindingInfo plane1{};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(metadata, plane0, plane1);
+    TINT_REFLECT(ExternalTexture, metadata, plane0, plane1);
 };
 
+/// Ensure that all the fields of ExternalTexture are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(ExternalTexture);
+
 }  // namespace binding
 
 // Maps the WGSL binding point to the SPIR-V group,binding for uniforms
@@ -107,9 +114,12 @@
     ExternalTextureBindings external_texture{};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(uniform, storage, texture, storage_texture, sampler, external_texture);
+    TINT_REFLECT(Bindings, uniform, storage, texture, storage_texture, sampler, external_texture);
 };
 
+/// Ensure that all the fields of Bindings are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Bindings);
+
 /// Configuration options used for generating SPIR-V.
 struct Options {
     /// Set to `true` to disable software robustness that prevents out-of-bounds accesses.
@@ -153,7 +163,8 @@
     Bindings bindings;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(disable_robustness,
+    TINT_REFLECT(Options,
+                 disable_robustness,
                  disable_image_robustness,
                  disable_runtime_sized_array_index_clamping,
                  disable_workgroup_init,
@@ -167,6 +178,9 @@
                  bindings);
 };
 
+/// Ensure that all the fields of Options are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Options);
+
 }  // namespace tint::spirv::writer
 
 #endif  // SRC_TINT_LANG_SPIRV_WRITER_COMMON_OPTIONS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override.h b/src/tint/lang/wgsl/ast/transform/substitute_override.h
index 2e18b11..c95426d 100644
--- a/src/tint/lang/wgsl/ast/transform/substitute_override.h
+++ b/src/tint/lang/wgsl/ast/transform/substitute_override.h
@@ -79,9 +79,12 @@
         std::unordered_map<OverrideId, double> map;
 
         /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-        TINT_REFLECT(map);
+        TINT_REFLECT(Config, map);
     };
 
+    /// Ensure that all the fields of Config are reflected.
+    TINT_ASSERT_ALL_FIELDS_REFLECTED(Config);
+
     /// Constructor
     SubstituteOverride();
 
diff --git a/src/tint/lang/wgsl/ast/transform/vertex_pulling.h b/src/tint/lang/wgsl/ast/transform/vertex_pulling.h
index 62ec75f..f238c0d 100644
--- a/src/tint/lang/wgsl/ast/transform/vertex_pulling.h
+++ b/src/tint/lang/wgsl/ast/transform/vertex_pulling.h
@@ -89,9 +89,12 @@
     uint32_t shader_location;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(format, offset, shader_location);
+    TINT_REFLECT(VertexAttributeDescriptor, format, offset, shader_location);
 };
 
+/// Ensure that all the fields of VertexAttributeDescriptor are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(VertexAttributeDescriptor);
+
 /// Describes a buffer containing multiple vertex attributes
 struct VertexBufferLayoutDescriptor {
     /// Constructor
@@ -122,9 +125,12 @@
     std::vector<VertexAttributeDescriptor> attributes;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(array_stride, step_mode, attributes);
+    TINT_REFLECT(VertexBufferLayoutDescriptor, array_stride, step_mode, attributes);
 };
 
+/// Ensure that all the fields of VertexBufferLayoutDescriptor are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(VertexBufferLayoutDescriptor);
+
 /// Describes vertex state, which consists of many buffers containing vertex
 /// attributes
 using VertexStateDescriptor = std::vector<VertexBufferLayoutDescriptor>;
@@ -176,9 +182,12 @@
         uint32_t pulling_group = 4u;
 
         /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-        TINT_REFLECT(vertex_state, pulling_group);
+        TINT_REFLECT(Config, vertex_state, pulling_group);
     };
 
+    /// Ensure that all the fields of Config are reflected.
+    TINT_ASSERT_ALL_FIELDS_REFLECTED(Config);
+
     /// Constructor
     VertexPulling();
 
diff --git a/src/tint/lang/wgsl/common/allowed_features.h b/src/tint/lang/wgsl/common/allowed_features.h
index 7549fc7..9673103 100644
--- a/src/tint/lang/wgsl/common/allowed_features.h
+++ b/src/tint/lang/wgsl/common/allowed_features.h
@@ -63,9 +63,12 @@
     }
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField().
-    TINT_REFLECT(extensions, features);
+    TINT_REFLECT(AllowedFeatures, extensions, features);
 };
 
+/// Ensure that all the fields of AllowedFeatures are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(AllowedFeatures);
+
 }  // namespace tint::wgsl
 
 #endif  // SRC_TINT_LANG_WGSL_COMMON_ALLOWED_FEATURES_H_
diff --git a/src/tint/lang/wgsl/reader/options.h b/src/tint/lang/wgsl/reader/options.h
index 3ac0e03..eb3400f 100644
--- a/src/tint/lang/wgsl/reader/options.h
+++ b/src/tint/lang/wgsl/reader/options.h
@@ -39,9 +39,12 @@
     AllowedFeatures allowed_features{};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField().
-    TINT_REFLECT(allowed_features);
+    TINT_REFLECT(Options, allowed_features);
 };
 
+/// Ensure that all the fields of Options are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Options);
+
 }  // namespace tint::wgsl::reader
 
 #endif  // SRC_TINT_LANG_WGSL_READER_OPTIONS_H_
diff --git a/src/tint/lang/wgsl/sem/sampler_texture_pair.h b/src/tint/lang/wgsl/sem/sampler_texture_pair.h
index f5896b6..6d3d6ae 100644
--- a/src/tint/lang/wgsl/sem/sampler_texture_pair.h
+++ b/src/tint/lang/wgsl/sem/sampler_texture_pair.h
@@ -68,9 +68,12 @@
     }
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(sampler_binding_point, texture_binding_point);
+    TINT_REFLECT(SamplerTexturePair, sampler_binding_point, texture_binding_point);
 };
 
+/// Ensure that all the fields of SamplerTexturePair are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(SamplerTexturePair);
+
 /// Prints the SamplerTexturePair @p stp to @p o
 /// @param o the stream to write to
 /// @param stp the SamplerTexturePair
diff --git a/src/tint/lang/wgsl/writer/options.h b/src/tint/lang/wgsl/writer/options.h
index 6fab6bb..09279a9 100644
--- a/src/tint/lang/wgsl/writer/options.h
+++ b/src/tint/lang/wgsl/writer/options.h
@@ -48,10 +48,15 @@
     /// Set to `true` to use the syntax tree writer
     bool use_syntax_tree_writer = false;
 
-    TINT_REFLECT(use_syntax_tree_writer);
+    TINT_REFLECT(Options, use_syntax_tree_writer);
 #endif
 };
 
+#ifdef TINT_BUILD_SYNTAX_TREE_WRITER
+/// Ensure that all the fields of SamplerTexturePair are reflected.
+TINT_ASSERT_ALL_FIELDS_REFLECTED(Options);
+#endif
+
 }  // namespace tint::wgsl::writer
 
 #endif  // SRC_TINT_LANG_WGSL_WRITER_OPTIONS_H_
diff --git a/src/tint/utils/bytes/decoder_test.cc b/src/tint/utils/bytes/decoder_test.cc
index 3535298..d33dc42 100644
--- a/src/tint/utils/bytes/decoder_test.cc
+++ b/src/tint/utils/bytes/decoder_test.cc
@@ -114,7 +114,7 @@
     uint8_t a;
     uint16_t b;
     uint32_t c;
-    TINT_REFLECT(a, b, c);
+    TINT_REFLECT(S, a, b, c);
 };
 
 TEST(BytesDecoderTest, ReflectedObject) {
diff --git a/src/tint/utils/reflection/BUILD.bazel b/src/tint/utils/reflection/BUILD.bazel
index e5a25ef..28efe09 100644
--- a/src/tint/utils/reflection/BUILD.bazel
+++ b/src/tint/utils/reflection/BUILD.bazel
@@ -46,6 +46,7 @@
   ],
   deps = [
     "//src/tint/utils/macros",
+    "//src/tint/utils/math",
   ],
   copts = COPTS,
   visibility = ["//visibility:public"],
@@ -58,7 +59,10 @@
   ],
   deps = [
     "//src/tint/utils/macros",
+    "//src/tint/utils/math",
     "//src/tint/utils/reflection",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/traits",
     "@gtest",
   ],
   copts = COPTS,
diff --git a/src/tint/utils/reflection/BUILD.cmake b/src/tint/utils/reflection/BUILD.cmake
index c2af545..143b121 100644
--- a/src/tint/utils/reflection/BUILD.cmake
+++ b/src/tint/utils/reflection/BUILD.cmake
@@ -45,6 +45,7 @@
 
 tint_target_add_dependencies(tint_utils_reflection lib
   tint_utils_macros
+  tint_utils_math
 )
 
 ################################################################################
@@ -57,7 +58,10 @@
 
 tint_target_add_dependencies(tint_utils_reflection_test test
   tint_utils_macros
+  tint_utils_math
   tint_utils_reflection
+  tint_utils_rtti
+  tint_utils_traits
 )
 
 tint_target_add_external_dependencies(tint_utils_reflection_test test
diff --git a/src/tint/utils/reflection/BUILD.gn b/src/tint/utils/reflection/BUILD.gn
index 42d0ab9..721544b 100644
--- a/src/tint/utils/reflection/BUILD.gn
+++ b/src/tint/utils/reflection/BUILD.gn
@@ -47,7 +47,10 @@
     "reflection.cc",
     "reflection.h",
   ]
-  deps = [ "${tint_src_dir}/utils/macros" ]
+  deps = [
+    "${tint_src_dir}/utils/macros",
+    "${tint_src_dir}/utils/math",
+  ]
 }
 if (tint_build_unittests) {
   tint_unittests_source_set("unittests") {
@@ -55,7 +58,10 @@
     deps = [
       "${tint_src_dir}:gmock_and_gtest",
       "${tint_src_dir}/utils/macros",
+      "${tint_src_dir}/utils/math",
       "${tint_src_dir}/utils/reflection",
+      "${tint_src_dir}/utils/rtti",
+      "${tint_src_dir}/utils/traits",
     ]
   }
 }
diff --git a/src/tint/utils/reflection/reflection.h b/src/tint/utils/reflection/reflection.h
index d674db1..7d192d8 100644
--- a/src/tint/utils/reflection/reflection.h
+++ b/src/tint/utils/reflection/reflection.h
@@ -28,10 +28,18 @@
 #ifndef SRC_TINT_UTILS_REFLECTION_REFLECTION_H_
 #define SRC_TINT_UTILS_REFLECTION_REFLECTION_H_
 
+#include <cstddef>
+#include <tuple>
 #include <type_traits>
 
 #include "src/tint/utils/macros/concat.h"
 #include "src/tint/utils/macros/foreach.h"
+#include "src/tint/utils/math/math.h"
+
+/// Forward declarations
+namespace tint {
+class CastableBase;
+}
 
 namespace tint {
 
@@ -45,6 +53,60 @@
 template <typename T>
 struct HasReflection<T, std::void_t<typename T::Reflection>> : std::true_type {};
 
+/// Helper for inferring the base class size of T.
+template <typename T, typename ENABLE = void>
+struct BaseClassSize {
+    /// Zero as T::Base does not exist
+    static constexpr size_t value = 0;
+};
+
+/// Specialization for types that contain a 'Base' type alias.
+template <typename T>
+struct BaseClassSize<T, std::void_t<typename T::Base>> {
+    /// The size of T::Base, or zero if T::Base is not a base of T
+    static constexpr size_t value =
+        std::is_base_of_v<typename T::Base, T> ? sizeof(typename T::Base) : 0;
+};
+
+/// A helper to check at compile-time that all the fields of a class are passed to TINT_REFLECT().
+template <typename CLASS, size_t INDEX, size_t OFFSET, typename FIELDS, bool ASSERT = true>
+struct CheckAllFieldsReflected;
+
+/// CheckAllFieldsReflected specialization that the final computed offset matches the size of the
+/// class.
+template <typename CLASS, size_t INDEX, size_t OFFSET, bool ASSERT>
+struct CheckAllFieldsReflected<CLASS, INDEX, OFFSET, std::tuple<void>, ASSERT> {
+    /// True iff the calculated size of class from all the fields matches the actual class size.
+    static constexpr bool value =
+        tint::RoundUp(alignof(CLASS), BaseClassSize<CLASS>::value + OFFSET) == sizeof(CLASS);
+    static_assert(value || (ASSERT == false),
+                  "TINT_REFLECT() was not passed all the fields of the class, or the fields were "
+                  "not passed in the same order they're declared in the class");
+};
+
+/// CheckAllFieldsReflected specialization that the field with index INDEX is at the expected offset
+/// in the class.
+template <typename CLASS,
+          size_t INDEX,
+          size_t OFFSET,
+          typename FIELD,
+          bool ASSERT,
+          typename... OTHERS>
+struct CheckAllFieldsReflected<CLASS, INDEX, OFFSET, std::tuple<FIELD, OTHERS...>, ASSERT> {
+    /// True iff the calculated size of class from all the fields matches the actual class size.
+    static constexpr bool value =
+        CheckAllFieldsReflected<CLASS,
+                                INDEX + 1,
+                                tint::RoundUp(alignof(FIELD), OFFSET) + sizeof(FIELD),
+                                std::tuple<OTHERS...>,
+                                ASSERT>::value;
+};
+
+/// Evaluates to true if type `T` can be used with TINT_ASSERT_ALL_FIELDS_REFLECTED().
+template <typename T>
+static constexpr bool CanUseTintAssertAllFieldsReflected =
+    !std::has_virtual_destructor_v<T> || std::is_base_of_v<CastableBase, T>;
+
 }  // namespace detail
 
 /// Is true if the class T has reflected its fields with TINT_REFLECT()
@@ -59,23 +121,39 @@
 void ForeachField(OBJECT&& object, CB&& callback) {
     using T = std::decay_t<OBJECT>;
     static_assert(HasReflection<T>, "object type requires a tint::Reflect<> specialization");
-    T::Reflection::Fields(object, callback);
+    T::Reflection::ForeachField(object, callback);
 }
 
+/// Macro used by TINT_FOREACH() in TINT_REFLECT() to generate the T::Reflection::Fields tuple.
+#define TINT_REFLECT_FIELD_TYPE(FIELD) decltype(Class::FIELD),
+
 /// Macro used by TINT_FOREACH() in TINT_REFLECT() to call the callback function with each field in
 /// the variadic.
-#define TINT_REFLECT_CALLBACK_FIELD(field) callback(object.field);
+#define TINT_REFLECT_CALLBACK_FIELD(FIELD) callback(object.FIELD);
 
-// TINT_REFLECT(...) reflects each of the fields arguments so that the types can be used with
-// tint::ForeachField().
-#define TINT_REFLECT(...)                                          \
-    struct Reflection {                                            \
-        template <typename OBJECT, typename CB>                    \
-        static void Fields(OBJECT&& object, CB&& callback) {       \
-            TINT_FOREACH(TINT_REFLECT_CALLBACK_FIELD, __VA_ARGS__) \
-        }                                                          \
+// TINT_REFLECT(CLASS, ...) reflects each of the fields arguments of CLASS so that the types can be
+// used with tint::ForeachField().
+#define TINT_REFLECT(CLASS, ...)                                                            \
+    struct Reflection {                                                                     \
+        using Class = CLASS;                                                                \
+        using Fields = std::tuple<TINT_FOREACH(TINT_REFLECT_FIELD_TYPE, __VA_ARGS__) void>; \
+        template <typename OBJECT, typename CB>                                             \
+        [[maybe_unused]] static void ForeachField(OBJECT&& object, CB&& callback) {         \
+            TINT_FOREACH(TINT_REFLECT_CALLBACK_FIELD, __VA_ARGS__)                          \
+        }                                                                                   \
     }
 
+/// TINT_ASSERT_ALL_FIELDS_REFLECTED(...) performs a compile-time assertion that all the fields of
+/// CLASS have been reflected with TINT_REFLECT().
+/// @note The order in which the fields are passed to TINT_REFLECT must match the declaration order
+/// in the class.
+#define TINT_ASSERT_ALL_FIELDS_REFLECTED(CLASS)                                                   \
+    static_assert(::tint::detail::CanUseTintAssertAllFieldsReflected<CLASS>,                      \
+                  "TINT_ASSERT_ALL_FIELDS_REFLECTED() cannot be used on virtual classes, except " \
+                  "for types using the tint::Castable framework");                                \
+    static_assert(                                                                                \
+        ::tint::detail::CheckAllFieldsReflected<CLASS, 0, 0, CLASS::Reflection::Fields>::value)
+
 /// A template that can be specialized to reflect the valid range of an enum
 /// Use TINT_REFLECT_ENUM_RANGE to specialize this class
 template <typename T>
diff --git a/src/tint/utils/reflection/reflection_test.cc b/src/tint/utils/reflection/reflection_test.cc
index d7335d5..558c1bf 100644
--- a/src/tint/utils/reflection/reflection_test.cc
+++ b/src/tint/utils/reflection/reflection_test.cc
@@ -27,6 +27,7 @@
 
 #include "src/tint/utils/reflection/reflection.h"
 #include "gtest/gtest.h"
+#include "src/tint/utils/rtti/castable.h"
 
 namespace tint {
 namespace {
@@ -35,7 +36,7 @@
     int i;
     unsigned u;
     bool b;
-    TINT_REFLECT(i, u, b);
+    TINT_REFLECT(S, i, u, b);
 };
 
 static_assert(!HasReflection<int>);
@@ -100,5 +101,57 @@
     EXPECT_EQ(s.b, false);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// TINT_ASSERT_ALL_FIELDS_REFLECTED tests
+////////////////////////////////////////////////////////////////////////////////
+static_assert(detail::CanUseTintAssertAllFieldsReflected<S> == true);
+
+struct VirtualNonCastable {
+    virtual ~VirtualNonCastable() = default;
+};
+static_assert(detail::CanUseTintAssertAllFieldsReflected<VirtualNonCastable> == false);
+
+struct VirtualCastable : Castable<VirtualCastable, CastableBase> {
+    ~VirtualCastable() override = default;
+    int a, b, c;
+    TINT_REFLECT(VirtualCastable, a, b, c);
+};
+static_assert(detail::CanUseTintAssertAllFieldsReflected<VirtualCastable> == true);
+static_assert(detail::CheckAllFieldsReflected<VirtualCastable,
+                                              0,
+                                              0,
+                                              VirtualCastable::Reflection::Fields,
+                                              /* assert */ false>::value == true);
+
+struct MissingFirst {
+    int a, b, c;
+    TINT_REFLECT(MissingFirst, b, c);
+};
+static_assert(detail::CheckAllFieldsReflected<MissingFirst,
+                                              0,
+                                              0,
+                                              MissingFirst::Reflection::Fields,
+                                              /* assert */ false>::value == false);
+
+struct MissingMid {
+    int a, b, c;
+    TINT_REFLECT(MissingMid, a, c);
+};
+static_assert(detail::CheckAllFieldsReflected<MissingMid,
+                                              0,
+                                              0,
+                                              MissingMid::Reflection::Fields,
+                                              /* assert */ false>::value == false);
+
+struct MissingLast {
+    int a, b, c;
+    TINT_REFLECT(MissingLast, a, b);
+};
+static_assert(detail::CheckAllFieldsReflected<MissingLast,
+                                              0,
+                                              0,
+                                              MissingLast::Reflection::Fields,
+                                              /* assert */ false>::value == false);
+
 }  // namespace
 }  // namespace tint