Import Tint changes from Dawn

Changes:
  - 394676bb7fb25598b36b9cb68b5eef16539d946c tint: Clean up CMakeLists.txt file list by Ben Clayton <bclayton@google.com>
  - 932418ef4637efa35ba8dd15e895acca540b921b tint: Implement abstract-numeric materialization by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 394676bb7fb25598b36b9cb68b5eef16539d946c
Change-Id: I0ac1104f8ec2dd501b17851673d1c2c92ef835b4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/91460
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index fbf443c..057433a 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -410,6 +410,7 @@
     "sem/if_statement.h",
     "sem/info.h",
     "sem/loop_statement.h",
+    "sem/materialize.h",
     "sem/matrix.h",
     "sem/module.h",
     "sem/multisampled_texture.h",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 85ec3db..1eef3a1 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -62,18 +62,17 @@
   ../../include/tint/tint.h
   ast/access.cc
   ast/access.h
-  ast/attribute.cc
-  ast/attribute.h
   ast/alias.cc
   ast/alias.h
-  ast/index_accessor_expression.cc
-  ast/index_accessor_expression.h
   ast/array.cc
   ast/array.h
   ast/assignment_statement.cc
   ast/assignment_statement.h
+  ast/ast_type.cc  # TODO(bclayton) - rename to type.cc
   ast/atomic.cc
   ast/atomic.h
+  ast/attribute.cc
+  ast/attribute.h
   ast/binary_expression.cc
   ast/binary_expression.h
   ast/binding_attribute.cc
@@ -104,10 +103,10 @@
   ast/continue_statement.h
   ast/depth_multisampled_texture.cc
   ast/depth_multisampled_texture.h
-  ast/disable_validation_attribute.cc
-  ast/disable_validation_attribute.h
   ast/depth_texture.cc
   ast/depth_texture.h
+  ast/disable_validation_attribute.cc
+  ast/disable_validation_attribute.h
   ast/discard_statement.cc
   ast/discard_statement.h
   ast/enable.cc
@@ -142,6 +141,8 @@
   ast/if_statement.h
   ast/increment_decrement_statement.cc
   ast/increment_decrement_statement.h
+  ast/index_accessor_expression.cc
+  ast/index_accessor_expression.h
   ast/int_literal_expression.cc
   ast/int_literal_expression.h
   ast/internal_attribute.cc
@@ -203,14 +204,11 @@
   ast/texture.cc
   ast/texture.h
   ast/traverse_expressions.h
-  ast/type_name.cc
-  ast/type_name.h
-  ast/ast_type.cc  # TODO(bclayton) - rename to type.cc
-  ast/type.h
   ast/type_decl.cc
   ast/type_decl.h
   ast/type_name.cc
   ast/type_name.h
+  ast/type.h
   ast/u32.cc
   ast/u32.h
   ast/unary_op_expression.cc
@@ -233,7 +231,6 @@
   clone_context.h
   demangler.cc
   demangler.h
-  number.h
   inspector/entry_point.cc
   inspector/entry_point.h
   inspector/inspector.cc
@@ -242,6 +239,7 @@
   inspector/resource_binding.h
   inspector/scalar.cc
   inspector/scalar.h
+  number.h
   program_builder.cc
   program_builder.h
   program_id.cc
@@ -257,8 +255,8 @@
   resolver/intrinsic_table.cc
   resolver/intrinsic_table.h
   resolver/intrinsic_table.inl
-  resolver/resolver.cc
   resolver/resolver_constants.cc
+  resolver/resolver.cc
   resolver/resolver.h
   resolver/sem_helper.cc
   resolver/sem_helper.h
@@ -282,6 +280,8 @@
   sem/binding_point.h
   sem/block_statement.cc
   sem/block_statement.h
+  sem/bool.cc
+  sem/bool.h
   sem/builtin_type.cc
   sem/builtin_type.h
   sem/builtin.cc
@@ -294,24 +294,74 @@
   sem/constant.h
   sem/depth_multisampled_texture.cc
   sem/depth_multisampled_texture.h
+  sem/depth_texture.cc
+  sem/depth_texture.h
   sem/expression.cc
   sem/expression.h
+  sem/external_texture.cc
+  sem/external_texture.h
+  sem/f16.cc
+  sem/f16.h
+  sem/f32.cc
+  sem/f32.h
+  sem/for_loop_statement.cc
+  sem/for_loop_statement.h
   sem/function.cc
+  sem/i32.cc
+  sem/i32.h
+  sem/if_statement.cc
+  sem/if_statement.h
   sem/info.cc
   sem/info.h
+  sem/loop_statement.cc
+  sem/loop_statement.h
+  sem/materialize.cc
+  sem/materialize.h
+  sem/matrix.cc
+  sem/matrix.h
   sem/member_accessor_expression.cc
+  sem/module.cc
+  sem/module.h
+  sem/multisampled_texture.cc
+  sem/multisampled_texture.h
+  sem/node.cc
+  sem/node.h
   sem/parameter_usage.cc
   sem/parameter_usage.h
   sem/pipeline_stage_set.h
-  sem/node.cc
-  sem/node.h
-  sem/module.cc
-  sem/module.h
+  sem/pointer.cc
+  sem/pointer.h
+  sem/reference.cc
+  sem/reference.h
+  sem/sampled_texture.cc
+  sem/sampled_texture.h
   sem/sampler_texture_pair.h
+  sem/sampler.cc
+  sem/sampler.h
   sem/statement.cc
+  sem/storage_texture.cc
+  sem/storage_texture.h
   sem/struct.cc
+  sem/switch_statement.cc
+  sem/switch_statement.h
+  sem/texture.cc
+  sem/texture.h
+  sem/type_constructor.cc
+  sem/type_constructor.h
+  sem/type_conversion.cc
+  sem/type_conversion.h
+  sem/type_manager.cc
+  sem/type_manager.h
   sem/type_mappings.h
+  sem/type.cc
+  sem/type.h
+  sem/u32.cc
+  sem/u32.h
   sem/variable.cc
+  sem/vector.cc
+  sem/vector.h
+  sem/void.cc
+  sem/void.h
   symbol_table.cc
   symbol_table.h
   symbol.cc
@@ -331,10 +381,10 @@
   transform/builtin_polyfill.h
   transform/calculate_array_length.cc
   transform/calculate_array_length.h
-  transform/combine_samplers.cc
-  transform/combine_samplers.h
   transform/canonicalize_entry_point_io.cc
   transform/canonicalize_entry_point_io.h
+  transform/combine_samplers.cc
+  transform/combine_samplers.h
   transform/decompose_memory_access.cc
   transform/decompose_memory_access.h
   transform/decompose_strided_array.cc
@@ -343,18 +393,18 @@
   transform/decompose_strided_matrix.h
   transform/disable_uniformity_analysis.cc
   transform/disable_uniformity_analysis.h
+  transform/expand_compound_assignment.cc
+  transform/expand_compound_assignment.h
   transform/first_index_offset.cc
   transform/first_index_offset.h
   transform/fold_constants.cc
   transform/fold_constants.h
   transform/fold_trivial_single_use_lets.cc
   transform/fold_trivial_single_use_lets.h
-  transform/localize_struct_array_assignment.cc
-  transform/localize_struct_array_assignment.h
   transform/for_loop_to_loop.cc
   transform/for_loop_to_loop.h
-  transform/expand_compound_assignment.cc
-  transform/expand_compound_assignment.h
+  transform/localize_struct_array_assignment.cc
+  transform/localize_struct_array_assignment.h
   transform/loop_to_for_loop.cc
   transform/loop_to_for_loop.h
   transform/manager.cc
@@ -369,10 +419,10 @@
   transform/promote_initializers_to_const_var.h
   transform/promote_side_effects_to_decl.cc
   transform/promote_side_effects_to_decl.h
-  transform/remove_phonies.cc
-  transform/remove_phonies.h
   transform/remove_continue_in_switch.cc
   transform/remove_continue_in_switch.h
+  transform/remove_phonies.cc
+  transform/remove_phonies.h
   transform/remove_unreachable_statements.cc
   transform/remove_unreachable_statements.h
   transform/renamer.cc
@@ -389,72 +439,20 @@
   transform/unshadow.h
   transform/unwind_discard_functions.cc
   transform/unwind_discard_functions.h
-  transform/vectorize_scalar_matrix_constructors.cc
-  transform/vectorize_scalar_matrix_constructors.h
+  transform/utils/get_insertion_point.cc
+  transform/utils/get_insertion_point.h
+  transform/utils/hoist_to_decl_before.cc
+  transform/utils/hoist_to_decl_before.h
   transform/var_for_dynamic_index.cc
   transform/var_for_dynamic_index.h
+  transform/vectorize_scalar_matrix_constructors.cc
+  transform/vectorize_scalar_matrix_constructors.h
   transform/vertex_pulling.cc
   transform/vertex_pulling.h
   transform/wrap_arrays_in_structs.cc
   transform/wrap_arrays_in_structs.h
   transform/zero_init_workgroup_memory.cc
   transform/zero_init_workgroup_memory.h
-  transform/utils/get_insertion_point.cc
-  transform/utils/get_insertion_point.h
-  transform/utils/hoist_to_decl_before.cc
-  transform/utils/hoist_to_decl_before.h
-  sem/bool.cc
-  sem/bool.h
-  sem/depth_texture.cc
-  sem/depth_texture.h
-  sem/external_texture.cc
-  sem/external_texture.h
-  sem/f16.cc
-  sem/f16.h
-  sem/f32.cc
-  sem/f32.h
-  sem/for_loop_statement.cc
-  sem/for_loop_statement.h
-  sem/i32.cc
-  sem/i32.h
-  sem/if_statement.cc
-  sem/if_statement.h
-  sem/loop_statement.cc
-  sem/loop_statement.h
-  sem/materialize.cc
-  sem/materialize.h
-  sem/matrix.cc
-  sem/matrix.h
-  sem/multisampled_texture.cc
-  sem/multisampled_texture.h
-  sem/pointer.cc
-  sem/pointer.h
-  sem/reference.cc
-  sem/reference.h
-  sem/sampled_texture.cc
-  sem/sampled_texture.h
-  sem/sampler.cc
-  sem/sampler.h
-  sem/storage_texture.cc
-  sem/storage_texture.h
-  sem/switch_statement.cc
-  sem/switch_statement.h
-  sem/texture.cc
-  sem/texture.h
-  sem/type_constructor.cc
-  sem/type_constructor.h
-  sem/type_conversion.cc
-  sem/type_conversion.h
-  sem/type.cc
-  sem/type.h
-  sem/type_manager.cc
-  sem/type_manager.h
-  sem/u32.cc
-  sem/u32.h
-  sem/vector.cc
-  sem/vector.h
-  sem/void.cc
-  sem/void.h
   utils/bitcast.h
   utils/block_allocator.h
   utils/crc32.h
@@ -774,6 +772,7 @@
     resolver/intrinsic_table_test.cc
     resolver/is_host_shareable_test.cc
     resolver/is_storeable_test.cc
+    resolver/materialize_test.cc
     resolver/pipeline_overridable_constant_test.cc
     resolver/ptr_ref_test.cc
     resolver/ptr_ref_validation_test.cc
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index 5a8985f..312f952 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -43,17 +43,17 @@
     struct UnaryOperator {
         /// The result type of the unary operator
         const sem::Type* result;
-        /// The type of the arg of the unary operator
-        const sem::Type* arg;
+        /// The type of the parameter of the unary operator
+        const sem::Type* parameter;
     };
 
     /// BinaryOperator describes a resolved binary operator
     struct BinaryOperator {
         /// The result type of the binary operator
         const sem::Type* result;
-        /// The type of LHS of the binary operator
+        /// The type of LHS parameter of the binary operator
         const sem::Type* lhs;
-        /// The type of RHS of the binary operator
+        /// The type of RHS parameter of the binary operator
         const sem::Type* rhs;
     };
 
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
new file mode 100644
index 0000000..e2e1a02
--- /dev/null
+++ b/src/tint/resolver/materialize_test.cc
@@ -0,0 +1,391 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/sem/materialize.h"
+
+#include "src/tint/resolver/resolver.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/test_helper.h"
+
+#include "gmock/gmock.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+using AFloatV = builder::vec<3, AFloat>;
+using AFloatM = builder::mat<3, 2, AFloat>;
+using AIntV = builder::vec<3, AInt>;
+using f32V = builder::vec<3, f32>;
+using f16V = builder::vec<3, f16>;
+using i32V = builder::vec<3, i32>;
+using u32V = builder::vec<3, u32>;
+using f32M = builder::mat<3, 2, f32>;
+
+////////////////////////////////////////////////////////////////////////////////
+// MaterializeTests
+////////////////////////////////////////////////////////////////////////////////
+namespace MaterializeTests {
+
+// How should the materialization occur?
+enum class Method {
+    // var a : T = literal;
+    kVar,
+
+    // let a : T = literal;
+    kLet,
+
+    // fn F(v : T) {}
+    // fn x() {
+    //   F(literal);
+    // }
+    kFnArg,
+
+    // min(target_expr, literal);
+    kBuiltinArg,
+
+    // fn F() : T {
+    //   return literal;
+    // }
+    kReturn,
+
+    // array<T, 1>(literal);
+    kArray,
+
+    // struct S {
+    //   v : T
+    // };
+    // fn x() {
+    //   _ = S(literal)
+    // }
+    kStruct,
+
+    // target_expr + literal
+    kBinaryOp,
+
+    // switch (literal) {
+    //   case target_expr: {}
+    //   default: {}
+    // }
+    kSwitchCond,
+
+    // switch (target_expr) {
+    //   case literal: {}
+    //   default: {}
+    // }
+    kSwitchCase,
+
+    // switch (literal) {
+    //   case 123: {}
+    //   case target_expr: {}
+    //   default: {}
+    // }
+    kSwitchCondWithAbstractCase,
+
+    // switch (target_expr) {
+    //   case 123: {}
+    //   case literal: {}
+    //   default: {}
+    // }
+    kSwitchCaseWithAbstractCase,
+};
+
+static std::ostream& operator<<(std::ostream& o, Method m) {
+    switch (m) {
+        case Method::kVar:
+            return o << "var";
+        case Method::kLet:
+            return o << "let";
+        case Method::kFnArg:
+            return o << "fn-arg";
+        case Method::kBuiltinArg:
+            return o << "builtin-arg";
+        case Method::kReturn:
+            return o << "return";
+        case Method::kArray:
+            return o << "array";
+        case Method::kStruct:
+            return o << "struct";
+        case Method::kBinaryOp:
+            return o << "binary-op";
+        case Method::kSwitchCond:
+            return o << "switch-cond";
+        case Method::kSwitchCase:
+            return o << "switch-case";
+        case Method::kSwitchCondWithAbstractCase:
+            return o << "switch-cond-with-abstract";
+        case Method::kSwitchCaseWithAbstractCase:
+            return o << "switch-case-with-abstract";
+    }
+    return o << "<unknown>";
+}
+
+struct Data {
+    std::string target_type_name;
+    builder::ast_type_func_ptr target_ast_ty;
+    builder::sem_type_func_ptr target_sem_ty;
+    builder::ast_expr_func_ptr target_expr;
+    std::string literal_type_name;
+    builder::ast_expr_func_ptr literal_value;
+    std::variant<AInt, AFloat> materialized_value;
+};
+
+template <typename TARGET_TYPE, typename LITERAL_TYPE, typename MATERIALIZED_TYPE = AInt>
+Data Types(MATERIALIZED_TYPE materialized_value = 0_a) {
+    return {
+        builder::DataType<TARGET_TYPE>::Name(),   //
+        builder::DataType<TARGET_TYPE>::AST,      //
+        builder::DataType<TARGET_TYPE>::Sem,      //
+        builder::DataType<TARGET_TYPE>::Expr,     //
+        builder::DataType<LITERAL_TYPE>::Name(),  //
+        builder::DataType<LITERAL_TYPE>::Expr,    //
+        materialized_value,
+    };
+}
+
+static std::ostream& operator<<(std::ostream& o, const Data& c) {
+    return o << "[" << c.target_type_name << " <- " << c.literal_type_name << "]";
+}
+
+enum class Expectation {
+    kMaterialize,
+    kNoMaterialize,
+    kInvalidCast,
+};
+
+static std::ostream& operator<<(std::ostream& o, Expectation m) {
+    switch (m) {
+        case Expectation::kMaterialize:
+            return o << "pass";
+        case Expectation::kNoMaterialize:
+            return o << "no-materialize";
+        case Expectation::kInvalidCast:
+            return o << "invalid-cast";
+    }
+    return o << "<unknown>";
+}
+
+using MaterializeAbstractNumeric =
+    resolver::ResolverTestWithParam<std::tuple<Expectation, Method, Data>>;
+
+TEST_P(MaterializeAbstractNumeric, Test) {
+    // Once F16 is properly supported, we'll need to enable this:
+    // Enable(ast::Extension::kF16);
+
+    const auto& param = GetParam();
+    const auto& expectation = std::get<0>(param);
+    const auto& method = std::get<1>(param);
+    const auto& data = std::get<2>(param);
+
+    auto target_ty = [&] { return data.target_ast_ty(*this); };
+    auto target_expr = [&] { return data.target_expr(*this, 42); };
+    auto* literal = data.literal_value(*this, 1);
+    switch (method) {
+        case Method::kVar:
+            WrapInFunction(Decl(Var("a", target_ty(), literal)));
+            break;
+        case Method::kLet:
+            WrapInFunction(Decl(Let("a", target_ty(), literal)));
+            break;
+        case Method::kFnArg:
+            Func("F", {Param("P", target_ty())}, ty.void_(), {});
+            WrapInFunction(CallStmt(Call("F", literal)));
+            break;
+        case Method::kBuiltinArg:
+            WrapInFunction(CallStmt(Call("min", target_expr(), literal)));
+            break;
+        case Method::kReturn:
+            Func("F", {}, target_ty(), {Return(literal)});
+            break;
+        case Method::kArray:
+            WrapInFunction(Construct(ty.array(target_ty(), 1_i), literal));
+            break;
+        case Method::kStruct:
+            Structure("S", {Member("v", target_ty())});
+            WrapInFunction(Construct(ty.type_name("S"), literal));
+            break;
+        case Method::kBinaryOp:
+            WrapInFunction(Add(target_expr(), literal));
+            break;
+        case Method::kSwitchCond:
+            WrapInFunction(Switch(literal,                                               //
+                                  Case(target_expr()->As<ast::IntLiteralExpression>()),  //
+                                  DefaultCase()));
+            break;
+        case Method::kSwitchCase:
+            WrapInFunction(Switch(target_expr(),                                   //
+                                  Case(literal->As<ast::IntLiteralExpression>()),  //
+                                  DefaultCase()));
+            break;
+        case Method::kSwitchCondWithAbstractCase:
+            WrapInFunction(Switch(literal,                                               //
+                                  Case(Expr(123_a)),                                     //
+                                  Case(target_expr()->As<ast::IntLiteralExpression>()),  //
+                                  DefaultCase()));
+            break;
+        case Method::kSwitchCaseWithAbstractCase:
+            WrapInFunction(Switch(target_expr(),                                   //
+                                  Case(Expr(123_a)),                               //
+                                  Case(literal->As<ast::IntLiteralExpression>()),  //
+                                  DefaultCase()));
+            break;
+    }
+
+    auto check_types_and_values = [&](const sem::Expression* expr) {
+        auto* target_sem_ty = data.target_sem_ty(*this);
+
+        EXPECT_TYPE(expr->Type(), target_sem_ty);
+        EXPECT_TYPE(expr->ConstantValue().Type(), target_sem_ty);
+
+        uint32_t num_elems = 0;
+        const sem::Type* target_sem_el_ty = sem::Type::ElementOf(target_sem_ty, &num_elems);
+        EXPECT_TYPE(expr->ConstantValue().ElementType(), target_sem_el_ty);
+        std::visit(
+            [&](auto&& v) {
+                EXPECT_EQ(expr->ConstantValue().Elements(), sem::Constant::Scalars(num_elems, {v}));
+            },
+            data.materialized_value);
+    };
+
+    switch (expectation) {
+        case Expectation::kMaterialize: {
+            ASSERT_TRUE(r()->Resolve()) << r()->error();
+            auto* materialize = Sem().Get<sem::Materialize>(literal);
+            ASSERT_NE(materialize, nullptr);
+            check_types_and_values(materialize);
+            break;
+        }
+        case Expectation::kNoMaterialize: {
+            ASSERT_TRUE(r()->Resolve()) << r()->error();
+            auto* sem = Sem().Get(literal);
+            ASSERT_NE(sem, nullptr);
+            EXPECT_FALSE(sem->Is<sem::Materialize>());
+            check_types_and_values(sem);
+            break;
+        }
+        case Expectation::kInvalidCast: {
+            ASSERT_FALSE(r()->Resolve());
+            std::string expect;
+            switch (method) {
+                case Method::kBuiltinArg:
+                    expect = "error: no matching call to min(" + data.target_type_name + ", " +
+                             data.literal_type_name + ")";
+                    break;
+                case Method::kBinaryOp:
+                    expect = "error: no matching overload for operator + (" +
+                             data.target_type_name + ", " + data.literal_type_name + ")";
+                    break;
+                default:
+                    expect = "error: cannot convert value of type '" + data.literal_type_name +
+                             "' to type '" + data.target_type_name + "'";
+                    break;
+            }
+            EXPECT_THAT(r()->error(), testing::StartsWith(expect));
+            break;
+        }
+    }
+}
+
+// TODO(crbug.com/tint/1504): Test for abstract-numeric values not fitting in materialized types.
+
+INSTANTIATE_TEST_SUITE_P(MaterializeScalar,
+                         MaterializeAbstractNumeric,                                        //
+                         testing::Combine(testing::Values(Expectation::kMaterialize),       //
+                                          testing::Values(Method::kLet,                     //
+                                                          Method::kVar,                     //
+                                                          Method::kFnArg,                   //
+                                                          Method::kBuiltinArg,              //
+                                                          Method::kReturn,                  //
+                                                          Method::kArray,                   //
+                                                          Method::kStruct,                  //
+                                                          Method::kBinaryOp),               //
+                                          testing::Values(Types<i32, AInt>(1_a),            //
+                                                          Types<u32, AInt>(1_a),            //
+                                                          Types<f32, AFloat>(1.0_a)         //
+                                                          /* Types<f16, AFloat>(1.0_a), */  //
+                                                          /* Types<f16, AFloat>(1.0_a), */)));
+
+INSTANTIATE_TEST_SUITE_P(MaterializeVector,
+                         MaterializeAbstractNumeric,                                          //
+                         testing::Combine(testing::Values(Expectation::kMaterialize),         //
+                                          testing::Values(Method::kLet,                       //
+                                                          Method::kVar,                       //
+                                                          Method::kFnArg,                     //
+                                                          Method::kBuiltinArg,                //
+                                                          Method::kReturn,                    //
+                                                          Method::kArray,                     //
+                                                          Method::kStruct,                    //
+                                                          Method::kBinaryOp),                 //
+                                          testing::Values(Types<i32V, AIntV>(1_a),            //
+                                                          Types<u32V, AIntV>(1_a),            //
+                                                          Types<f32V, AFloatV>(1.0_a)         //
+                                                          /* Types<f16V, AFloatV>(1.0_a), */  //
+                                                          /* Types<f16V, AFloatV>(1.0_a), */)));
+
+INSTANTIATE_TEST_SUITE_P(MaterializeMatrix,
+                         MaterializeAbstractNumeric,                                          //
+                         testing::Combine(testing::Values(Expectation::kMaterialize),         //
+                                          testing::Values(Method::kLet,                       //
+                                                          Method::kVar,                       //
+                                                          Method::kFnArg,                     //
+                                                          Method::kReturn,                    //
+                                                          Method::kArray,                     //
+                                                          Method::kStruct,                    //
+                                                          Method::kBinaryOp),                 //
+                                          testing::Values(Types<f32M, AFloatM>(1.0_a)         //
+                                                          /* Types<f16V, AFloatM>(1.0_a), */  //
+                                                          )));
+
+INSTANTIATE_TEST_SUITE_P(MaterializeSwitch,
+                         MaterializeAbstractNumeric,                                             //
+                         testing::Combine(testing::Values(Expectation::kMaterialize),            //
+                                          testing::Values(Method::kSwitchCond,                   //
+                                                          Method::kSwitchCase,                   //
+                                                          Method::kSwitchCondWithAbstractCase,   //
+                                                          Method::kSwitchCaseWithAbstractCase),  //
+                                          testing::Values(Types<i32, AInt>(1_a),                 //
+                                                          Types<u32, AInt>(1_a))));
+
+// TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops.
+INSTANTIATE_TEST_SUITE_P(DISABLED_NoMaterialize,
+                         MaterializeAbstractNumeric,                                       //
+                         testing::Combine(testing::Values(Expectation::kNoMaterialize),    //
+                                          testing::Values(Method::kBuiltinArg,             //
+                                                          Method::kBinaryOp),              //
+                                          testing::Values(Types<AInt, AInt>(1_a),          //
+                                                          Types<AFloat, AFloat>(1.0_a),    //
+                                                          Types<AIntV, AIntV>(1_a),        //
+                                                          Types<AFloatV, AFloatV>(1.0_a),  //
+                                                          Types<AFloatM, AFloatM>(1.0_a))));
+INSTANTIATE_TEST_SUITE_P(InvalidCast,
+                         MaterializeAbstractNumeric,                                   //
+                         testing::Combine(testing::Values(Expectation::kInvalidCast),  //
+                                          testing::Values(Method::kLet,                //
+                                                          Method::kVar,                //
+                                                          Method::kFnArg,              //
+                                                          Method::kBuiltinArg,         //
+                                                          Method::kReturn,             //
+                                                          Method::kArray,              //
+                                                          Method::kStruct,             //
+                                                          Method::kBinaryOp),          //
+                                          testing::Values(Types<i32, AFloat>(),        //
+                                                          Types<u32, AFloat>(),        //
+                                                          Types<i32V, AFloatV>(),      //
+                                                          Types<u32V, AFloatV>())));
+
+}  // namespace MaterializeTests
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 270acf1..9d3d7a4 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -62,6 +62,7 @@
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/if_statement.h"
 #include "src/tint/sem/loop_statement.h"
+#include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/module.h"
 #include "src/tint/sem/multisampled_texture.h"
@@ -318,7 +319,11 @@
 
     // Does the variable have a constructor?
     if (var->constructor) {
-        rhs = Expression(var->constructor);
+        auto* ctor = Expression(var->constructor);
+        if (!ctor) {
+            return nullptr;
+        }
+        rhs = Materialize(ctor, storage_ty);
         if (!rhs) {
             return nullptr;
         }
@@ -1100,6 +1105,83 @@
     return nullptr;
 }
 
+const sem::Expression* Resolver::Materialize(const sem::Expression* expr,
+                                             const sem::Type* target_type /* = nullptr */) {
+    // Helper for actually creating the the materialize node, performing the constant cast, updating
+    // the ast -> sem binding, and performing validation.
+    auto materialize = [&](const sem::Type* target_ty) -> sem::Materialize* {
+        auto expr_val = EvaluateConstantValue(expr->Declaration(), expr->Type());
+        if (!expr_val.IsValid()) {
+            TINT_ICE(Resolver, builder_->Diagnostics())
+                << expr->Declaration()->source
+                << " EvaluateConstantValue() returned invalid value for materialized "
+                   "value of type: "
+                << (expr->Type() ? expr->Type()->FriendlyName(builder_->Symbols()) : "<null>");
+            return nullptr;
+        }
+        auto materialized_val = ConstantCast(expr_val, target_ty);
+        auto* m = builder_->create<sem::Materialize>(expr, current_statement_, materialized_val);
+        m->Behaviors() = expr->Behaviors();
+        builder_->Sem().Replace(expr->Declaration(), m);
+        return validator_.Materialize(m) ? m : nullptr;
+    };
+
+    // Helpers for constructing semantic types
+    auto i32 = [&] { return builder_->create<sem::I32>(); };
+    auto f32 = [&] { return builder_->create<sem::F32>(); };
+    auto i32v = [&](uint32_t width) { return builder_->create<sem::Vector>(i32(), width); };
+    auto f32v = [&](uint32_t width) { return builder_->create<sem::Vector>(f32(), width); };
+    auto f32m = [&](uint32_t columns, uint32_t rows) {
+        return builder_->create<sem::Matrix>(f32v(columns), rows);
+    };
+
+    // Type dispatch based on the expression type
+    return Switch<sem::Expression*>(
+        expr->Type(),  //
+        [&](const sem::AbstractInt*) { return materialize(target_type ? target_type : i32()); },
+        [&](const sem::AbstractFloat*) { return materialize(target_type ? target_type : f32()); },
+        [&](const sem::Vector* v) {
+            return Switch(
+                v->type(),  //
+                [&](const sem::AbstractInt*) {
+                    return materialize(target_type ? target_type : i32v(v->Width()));
+                },
+                [&](const sem::AbstractFloat*) {
+                    return materialize(target_type ? target_type : f32v(v->Width()));
+                },
+                [&](Default) { return expr; });
+        },
+        [&](const sem::Matrix* m) {
+            return Switch(
+                m->type(),  //
+                [&](const sem::AbstractFloat*) {
+                    return materialize(target_type ? target_type : f32m(m->columns(), m->rows()));
+                },
+                [&](Default) { return expr; });
+        },
+        [&](Default) { return expr; });
+}
+
+bool Resolver::MaterializeArguments(std::vector<const sem::Expression*>& args,
+                                    const sem::CallTarget* target) {
+    for (size_t i = 0, n = std::min(args.size(), target->Parameters().size()); i < n; i++) {
+        const auto* param_ty = target->Parameters()[i]->Type();
+        if (ShouldMaterializeArgument(param_ty)) {
+            auto* materialized = Materialize(args[i], param_ty);
+            if (!materialized) {
+                return false;
+            }
+            args[i] = materialized;
+        }
+    }
+    return true;
+}
+
+bool Resolver::ShouldMaterializeArgument(const sem::Type* parameter_ty) const {
+    const auto* param_el_ty = sem::Type::ElementOf(parameter_ty);
+    return param_el_ty && !param_el_ty->Is<sem::AbstractNumeric>();
+}
+
 sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* expr) {
     auto* idx = sem_.Get(expr->index);
     auto* obj = sem_.Get(expr->object);
@@ -1192,6 +1274,9 @@
         if (!call_target) {
             return nullptr;
         }
+        if (!MaterializeArguments(args, call_target)) {
+            return nullptr;
+        }
         auto value = EvaluateConstantValue(expr, call_target->ReturnType());
         return builder_->create<sem::Call>(expr, call_target, std::move(args), current_statement_,
                                            value, has_side_effects);
@@ -1227,6 +1312,9 @@
                         }
                         return builder_->create<sem::TypeConstructor>(arr, std::move(params));
                     });
+                if (!MaterializeArguments(args, call_target)) {
+                    return nullptr;
+                }
                 auto value = EvaluateConstantValue(expr, call_target->ReturnType());
                 return builder_->create<sem::Call>(expr, call_target, std::move(args),
                                                    current_statement_, value, has_side_effects);
@@ -1246,6 +1334,9 @@
                         }
                         return builder_->create<sem::TypeConstructor>(str, std::move(params));
                     });
+                if (!MaterializeArguments(args, call_target)) {
+                    return nullptr;
+                }
                 auto value = EvaluateConstantValue(expr, call_target->ReturnType());
                 return builder_->create<sem::Call>(expr, call_target, std::move(args),
                                                    current_statement_, value, has_side_effects);
@@ -1368,6 +1459,10 @@
         }
     }
 
+    if (!MaterializeArguments(args, builtin)) {
+        return nullptr;
+    }
+
     if (builtin->IsDeprecated()) {
         AddWarning("use of deprecated builtin", expr->source);
     }
@@ -1425,6 +1520,10 @@
     auto sym = expr->target.name->symbol;
     auto name = builder_->Symbols().NameFor(sym);
 
+    if (!MaterializeArguments(args, target)) {
+        return nullptr;
+    }
+
     // TODO(crbug.com/tint/1420): For now, assume all function calls have side
     // effects.
     bool has_side_effects = true;
@@ -1715,6 +1814,18 @@
     if (!op.result) {
         return nullptr;
     }
+    if (ShouldMaterializeArgument(op.lhs)) {
+        lhs = Materialize(lhs, op.lhs);
+        if (!lhs) {
+            return nullptr;
+        }
+    }
+    if (ShouldMaterializeArgument(op.rhs)) {
+        rhs = Materialize(rhs, op.rhs);
+        if (!rhs) {
+            return nullptr;
+        }
+    }
 
     auto val = EvaluateConstantValue(expr, op.result);
     bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
@@ -1775,10 +1886,17 @@
             break;
 
         default: {
-            ty = intrinsic_table_->Lookup(unary->op, expr_ty, unary->source).result;
-            if (!ty) {
+            auto op = intrinsic_table_->Lookup(unary->op, expr_ty, unary->source);
+            if (!op.result) {
                 return nullptr;
             }
+            if (ShouldMaterializeArgument(op.parameter)) {
+                expr = Materialize(expr, op.parameter);
+                if (!expr) {
+                    return nullptr;
+                }
+            }
+            ty = op.result;
             break;
         }
     }
@@ -2118,7 +2236,11 @@
 
         const sem::Type* value_ty = nullptr;
         if (auto* value = stmt->value) {
-            auto* expr = Expression(value);
+            const auto* expr = Expression(value);
+            if (!expr) {
+                return false;
+            }
+            expr = Materialize(expr, current_function_->ReturnType());
             if (!expr) {
                 return false;
             }
@@ -2141,22 +2263,54 @@
     return StatementScope(stmt, sem, [&] {
         auto& behaviors = sem->Behaviors();
 
-        auto* cond = Expression(stmt->condition);
+        const auto* cond = Expression(stmt->condition);
         if (!cond) {
             return false;
         }
         behaviors = cond->Behaviors() - sem::Behavior::kNext;
 
+        auto* cond_ty = cond->Type()->UnwrapRef();
+
+        utils::UniqueVector<const sem::Type*> types;
+        types.add(cond_ty);
+
+        std::vector<sem::CaseStatement*> cases;
+        cases.reserve(stmt->body.size());
         for (auto* case_stmt : stmt->body) {
             Mark(case_stmt);
             auto* c = CaseStatement(case_stmt);
             if (!c) {
                 return false;
             }
+            for (auto* expr : c->Selectors()) {
+                types.add(expr->Type()->UnwrapRef());
+            }
+            cases.emplace_back(c);
             behaviors.Add(c->Behaviors());
             sem->Cases().emplace_back(c);
         }
 
+        // Determine the common type across all selectors and the switch expression
+        // This must materialize to an integer scalar (non-abstract).
+        auto* common_ty = sem::Type::Common(types.data(), types.size());
+        if (!common_ty || !common_ty->is_integer_scalar()) {
+            // No common type found or the common type was abstract.
+            // Pick i32 and let validation deal with any mismatches.
+            common_ty = builder_->create<sem::I32>();
+        }
+        cond = Materialize(cond, common_ty);
+        if (!cond) {
+            return false;
+        }
+        for (auto* c : cases) {
+            for (auto*& sel : c->Selectors()) {  // Note: pointer reference
+                sel = Materialize(sel, common_ty);
+                if (!sel) {
+                    return false;
+                }
+            }
+        }
+
         if (behaviors.Contains(sem::Behavior::kBreak)) {
             behaviors.Add(sem::Behavior::kNext);
         }
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 1725072..b03bb32 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -198,6 +198,30 @@
     sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
     sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
 
+    /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
+    /// If `expr` is of an abstract-numeric type:
+    /// * Materialize will create and return a sem::Materialize node wrapping `expr`.
+    /// * The AST -> Sem binding will be updated to point to the new sem::Materialize node.
+    /// * The sem::Materialize node will have a new concrete type, which will be `target_type` if
+    ///   not nullptr, otherwise:
+    ///     * a type with the element type of `i32` (e.g. `i32`, `vec2<i32>`) if `expr` has a
+    ///       element type of abstract-integer...
+    ///     * ... or a type with the element type of `f32` (e.g. `f32`, vec3<f32>`, `mat2x3<f32>`)
+    ///       if `expr` has a element type of abstract-float.
+    /// * The sem::Materialize constant value will be the value of `expr` value-converted to the
+    ///   materialized type.
+    const sem::Expression* Materialize(const sem::Expression* expr,
+                                       const sem::Type* target_type = nullptr);
+
+    /// Materializes all the arguments in `args` to the parameter types of `target`.
+    /// @returns true on success, false on failure.
+    bool MaterializeArguments(std::vector<const sem::Expression*>& args,
+                              const sem::CallTarget* target);
+
+    /// @returns true if an argument of an abstract numeric type, passed to a parameter of type
+    /// `parameter_ty` should be materialized.
+    bool ShouldMaterializeArgument(const sem::Type* parameter_ty) const;
+
     // Statement resolving methods
     // Each return true on success, false on failure.
     sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 9f698e6..fdd74e9 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -57,6 +57,7 @@
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/if_statement.h"
 #include "src/tint/sem/loop_statement.h"
+#include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/multisampled_texture.h"
 #include "src/tint/sem/pointer.h"
@@ -276,6 +277,19 @@
     return true;
 }
 
+bool Validator::Materialize(const sem::Materialize* m) const {
+    auto* from = m->Expr()->Type();
+    auto* to = m->Type();
+
+    if (sem::Type::ConversionRank(from, to) == sem::Type::kNoConversion) {
+        AddError("cannot convert value of type '" + sem_.TypeNameOf(from) + "' to type '" +
+                     sem_.TypeNameOf(to) + "'",
+                 m->Expr()->Declaration()->source);
+        return false;
+    }
+    return true;
+}
+
 bool Validator::VariableConstructorOrCast(const ast::Variable* var,
                                           ast::StorageClass storage_class,
                                           const sem::Type* storage_ty,
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index a8c18d5..b30fdc7 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -54,6 +54,7 @@
 class ForLoopStatement;
 class IfStatement;
 class LoopStatement;
+class Materialize;
 class Statement;
 class SwitchStatement;
 class TypeConstructor;
@@ -275,6 +276,11 @@
     /// @returns true on success, false otherwise.
     bool LoopStatement(const sem::LoopStatement* stmt) const;
 
+    /// Validates a materialize of an abstract numeric value
+    /// @param m the materialize to validate
+    /// @returns true on success, false otherwise
+    bool Materialize(const sem::Materialize* m) const;
+
     /// Validates a matrix
     /// @param ty the matrix to validate
     /// @param source the source of the matrix
diff --git a/src/tint/sem/info.h b/src/tint/sem/info.h
index 66b2cd5..41321cf 100644
--- a/src/tint/sem/info.h
+++ b/src/tint/sem/info.h
@@ -70,8 +70,7 @@
         return As<RESULT>(it->second);
     }
 
-    /// Add registers the semantic node `sem_node` for the AST or type node
-    /// `node`.
+    /// Add registers the semantic node `sem_node` for the AST or type node `node`.
     /// @param node the AST or type node
     /// @param sem_node the semantic node
     template <typename AST_OR_TYPE>
@@ -81,6 +80,14 @@
         map_.emplace(node, sem_node);
     }
 
+    /// Replace replaces any existing semantic node `sem_node` for the AST or type node `node`.
+    /// @param node the AST or type node
+    /// @param sem_node the new semantic node
+    template <typename AST_OR_TYPE>
+    void Replace(const AST_OR_TYPE* node, const SemanticNodeTypeFor<AST_OR_TYPE>* sem_node) {
+        map_[node] = sem_node;
+    }
+
     /// Wrap returns a new Info created with the contents of `inner`.
     /// The Info returned by Wrap is intended to temporarily extend the contents
     /// of an existing immutable Info.
diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn
index 2ef1202..4f1619f 100644
--- a/test/tint/BUILD.gn
+++ b/test/tint/BUILD.gn
@@ -257,6 +257,7 @@
     "../../src/tint/resolver/intrinsic_table_test.cc",
     "../../src/tint/resolver/is_host_shareable_test.cc",
     "../../src/tint/resolver/is_storeable_test.cc",
+    "../../src/tint/resolver/materialize_test.cc",
     "../../src/tint/resolver/pipeline_overridable_constant_test.cc",
     "../../src/tint/resolver/ptr_ref_test.cc",
     "../../src/tint/resolver/ptr_ref_validation_test.cc",