tint/resolver: Add tests for implicit type materialization

These are tests for when there's no explicit target type for a
materialization. In this case we expect an abstract-int to
materialize to an i32 and an abstract-float to materialize to a
f32.

Fix a bug uncovered where we were creating a transposed matrix.

Bug: tint:1504
Change-Id: Ie69dd7ec47174d3d7bef20315fd3780dade3a325
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91845
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index 6573426..2bd74cc 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -34,10 +34,44 @@
 using u32V = builder::vec<3, u32>;
 using f32M = builder::mat<3, 2, f32>;
 
-////////////////////////////////////////////////////////////////////////////////
-// MaterializeTests
-////////////////////////////////////////////////////////////////////////////////
-namespace MaterializeTests {
+constexpr double kHighestU32 = static_cast<double>(u32::kHighest);
+constexpr double kLowestU32 = static_cast<double>(u32::kLowest);
+constexpr double kHighestI32 = static_cast<double>(i32::kHighest);
+constexpr double kLowestI32 = static_cast<double>(i32::kLowest);
+constexpr double kHighestF32 = static_cast<double>(f32::kHighest);
+constexpr double kLowestF32 = static_cast<double>(f32::kLowest);
+constexpr double kTooBigF32 = static_cast<double>(3.5e+38);
+constexpr double kPiF64 = 3.141592653589793;
+constexpr double kPiF32 = 3.1415927410125732;  // kPiF64 quantized to f32
+
+constexpr double kSubnormalF32 = 0x1.0p-128;
+
+enum class Expectation {
+    kMaterialize,
+    kNoMaterialize,
+    kInvalidConversion,
+    kValueCannotBeRepresented,
+};
+
+static std::ostream& operator<<(std::ostream& o, Expectation m) {
+    switch (m) {
+        case Expectation::kMaterialize:
+            return o << "materialize";
+        case Expectation::kNoMaterialize:
+            return o << "no-materialize";
+        case Expectation::kInvalidConversion:
+            return o << "invalid-conversion";
+        case Expectation::kValueCannotBeRepresented:
+            return o << "value cannot be represented";
+    }
+    return o << "<unknown>";
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// MaterializeAbstractNumericToConcreteType
+// Tests that an abstract-numeric will materialize to the expected concrete type
+////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace materialize_abstract_numeric_to_concrete_type {
 
 // How should the materialization occur?
 enum class Method {
@@ -188,31 +222,10 @@
     return o;
 }
 
-enum class Expectation {
-    kMaterialize,
-    kNoMaterialize,
-    kInvalidCast,
-    kValueCannotBeRepresented,
-};
-
-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";
-        case Expectation::kValueCannotBeRepresented:
-            return o << "value too low or high";
-    }
-    return o << "<unknown>";
-}
-
-using MaterializeAbstractNumeric =
+using MaterializeAbstractNumericToConcreteType =
     resolver::ResolverTestWithParam<std::tuple<Expectation, Method, Data>>;
 
-TEST_P(MaterializeAbstractNumeric, Test) {
+TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
     // Once F16 is properly supported, we'll need to enable this:
     // Enable(ast::Extension::kF16);
 
@@ -309,7 +322,7 @@
             check_types_and_values(sem);
             break;
         }
-        case Expectation::kInvalidCast: {
+        case Expectation::kInvalidConversion: {
             ASSERT_FALSE(r()->Resolve());
             std::string expect;
             switch (method) {
@@ -338,170 +351,484 @@
 }
 
 /// Methods that support scalar materialization
-constexpr Method kScalarMethods[] = {Method::kLet,         //
-                                     Method::kVar,         //
-                                     Method::kFnArg,       //
-                                     Method::kBuiltinArg,  //
-                                     Method::kReturn,      //
-                                     Method::kArray,       //
-                                     Method::kStruct,      //
-                                     Method::kBinaryOp};
+constexpr Method kScalarMethods[] = {
+    Method::kLet,    Method::kVar,   Method::kFnArg,  Method::kBuiltinArg,
+    Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
+};
 
 /// Methods that support vector materialization
-constexpr Method kVectorMethods[] = {Method::kLet,         //
-                                     Method::kVar,         //
-                                     Method::kFnArg,       //
-                                     Method::kBuiltinArg,  //
-                                     Method::kReturn,      //
-                                     Method::kArray,       //
-                                     Method::kStruct,      //
-                                     Method::kBinaryOp};
+constexpr Method kVectorMethods[] = {
+    Method::kLet,    Method::kVar,   Method::kFnArg,  Method::kBuiltinArg,
+    Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
+};
 
 /// Methods that support matrix materialization
-constexpr Method kMatrixMethods[] = {Method::kLet,     //
-                                     Method::kVar,     //
-                                     Method::kFnArg,   //
-                                     Method::kReturn,  //
-                                     Method::kArray,   //
-                                     Method::kStruct,  //
-                                     Method::kBinaryOp};
+constexpr Method kMatrixMethods[] = {
+    Method::kLet,   Method::kVar,    Method::kFnArg,    Method::kReturn,
+    Method::kArray, Method::kStruct, Method::kBinaryOp,
+};
 
 /// Methods that support materialization for switch cases
-constexpr Method kSwitchMethods[] = {Method::kSwitchCond,                  //
-                                     Method::kSwitchCase,                  //
-                                     Method::kSwitchCondWithAbstractCase,  //
-                                     Method::kSwitchCaseWithAbstractCase};
-
-constexpr double kMaxF32 = static_cast<double>(f32::kHighest);
-constexpr double kPiF64 = 3.141592653589793;
-constexpr double kPiF32 = 3.1415927410125732;  // kPiF64 quantized to f32
-
-constexpr double kSubnormalF32 = 0x1.0p-128;
+constexpr Method kSwitchMethods[] = {
+    Method::kSwitchCond,
+    Method::kSwitchCase,
+    Method::kSwitchCondWithAbstractCase,
+    Method::kSwitchCaseWithAbstractCase,
+};
 
 INSTANTIATE_TEST_SUITE_P(
     MaterializeScalar,
-    MaterializeAbstractNumeric,                                                                  //
-    testing::Combine(testing::Values(Expectation::kMaterialize),                                 //
-                     testing::ValuesIn(kScalarMethods),                                          //
-                     testing::Values(Types<i32, AInt>(0_a, 0.0),                                 //
-                                     Types<i32, AInt>(2147483647_a, 2147483647.0),               //
-                                     Types<i32, AInt>(-2147483648_a, -2147483648.0),             //
-                                     Types<u32, AInt>(0_a, 0.0),                                 //
-                                     Types<u32, AInt>(4294967295_a, 4294967295.0),               //
-                                     Types<f32, AFloat>(0.0_a, 0.0),                             //
-                                     Types<f32, AFloat>(AFloat(kMaxF32), kMaxF32),               //
-                                     Types<f32, AFloat>(AFloat(-kMaxF32), -kMaxF32),             //
-                                     Types<f32, AFloat>(AFloat(kPiF32), kPiF64),                 //
-                                     Types<f32, AFloat>(AFloat(kSubnormalF32), kSubnormalF32),   //
-                                     Types<f32, AFloat>(AFloat(-kSubnormalF32), -kSubnormalF32)  //
-                                     /* Types<f16, AFloat>(1.0_a), */                            //
-                                     /* Types<f16, AFloat>(1.0_a), */)));
+    MaterializeAbstractNumericToConcreteType,
+    testing::Combine(testing::Values(Expectation::kMaterialize),
+                     testing::ValuesIn(kScalarMethods),
+                     testing::ValuesIn(std::vector<Data>{
+                         Types<i32, AInt>(0_a, 0.0),                                  //
+                         Types<i32, AInt>(1_a, 1.0),                                  //
+                         Types<i32, AInt>(-1_a, -1.0),                                //
+                         Types<i32, AInt>(AInt(kHighestI32), kHighestI32),            //
+                         Types<i32, AInt>(AInt(kLowestI32), kLowestI32),              //
+                         Types<u32, AInt>(0_a, 0.0),                                  //
+                         Types<u32, AInt>(1_a, 1.0),                                  //
+                         Types<u32, AInt>(AInt(kHighestU32), kHighestU32),            //
+                         Types<u32, AInt>(AInt(kLowestU32), kLowestU32),              //
+                         Types<f32, AFloat>(0.0_a, 0.0),                              //
+                         Types<f32, AFloat>(AFloat(kHighestF32), kHighestF32),        //
+                         Types<f32, AFloat>(AFloat(kLowestF32), kLowestF32),          //
+                         Types<f32, AFloat>(AFloat(kPiF32), kPiF64),                  //
+                         Types<f32, AFloat>(AFloat(kSubnormalF32), kSubnormalF32),    //
+                         Types<f32, AFloat>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
+                         /* 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::ValuesIn(kVectorMethods),                                           //
-                     testing::Values(Types<i32V, AIntV>(0_a, 0.0),                                //
-                                     Types<i32V, AIntV>(2147483647_a, 2147483647.0),              //
-                                     Types<i32V, AIntV>(-2147483648_a, -2147483648.0),            //
-                                     Types<u32V, AIntV>(0_a, 0.0),                                //
-                                     Types<u32V, AIntV>(4294967295_a, 4294967295.0),              //
-                                     Types<f32V, AFloatV>(0.0_a, 0.0),                            //
-                                     Types<f32V, AFloatV>(AFloat(kMaxF32), kMaxF32),              //
-                                     Types<f32V, AFloatV>(AFloat(-kMaxF32), -kMaxF32),            //
-                                     Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64),                //
-                                     Types<f32V, AFloatV>(AFloat(kSubnormalF32), kSubnormalF32),  //
-                                     Types<f32V, AFloatV>(AFloat(-kSubnormalF32),
-                                                          -kSubnormalF32)  //
-                                     /* Types<f16V, AFloatV>(1.0_a), */    //
-                                     /* Types<f16V, AFloatV>(1.0_a), */)));
+    MaterializeAbstractNumericToConcreteType,
+    testing::Combine(testing::Values(Expectation::kMaterialize),
+                     testing::ValuesIn(kVectorMethods),
+                     testing::ValuesIn(std::vector<Data>{
+                         Types<i32V, AIntV>(0_a, 0.0),                                  //
+                         Types<i32V, AIntV>(1_a, 1.0),                                  //
+                         Types<i32V, AIntV>(-1_a, -1.0),                                //
+                         Types<i32V, AIntV>(AInt(kHighestI32), kHighestI32),            //
+                         Types<i32V, AIntV>(AInt(kLowestI32), kLowestI32),              //
+                         Types<u32V, AIntV>(0_a, 0.0),                                  //
+                         Types<u32V, AIntV>(1_a, 1.0),                                  //
+                         Types<u32V, AIntV>(AInt(kHighestU32), kHighestU32),            //
+                         Types<u32V, AIntV>(AInt(kLowestU32), kLowestU32),              //
+                         Types<f32V, AFloatV>(0.0_a, 0.0),                              //
+                         Types<f32V, AFloatV>(1.0_a, 1.0),                              //
+                         Types<f32V, AFloatV>(-1.0_a, -1.0),                            //
+                         Types<f32V, AFloatV>(AFloat(kHighestF32), kHighestF32),        //
+                         Types<f32V, AFloatV>(AFloat(kLowestF32), kLowestF32),          //
+                         Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64),                  //
+                         Types<f32V, AFloatV>(AFloat(kSubnormalF32), kSubnormalF32),    //
+                         Types<f32V, AFloatV>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
+                         /* 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::ValuesIn(kMatrixMethods),                                           //
-                     testing::Values(Types<f32M, AFloatM>(0.0_a, 0.0),                            //
-                                     Types<f32M, AFloatM>(AFloat(kMaxF32), kMaxF32),              //
-                                     Types<f32M, AFloatM>(AFloat(-kMaxF32), -kMaxF32),            //
-                                     Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64),                //
-                                     Types<f32M, AFloatM>(AFloat(kSubnormalF32), kSubnormalF32),  //
-                                     Types<f32M, AFloatM>(AFloat(-kSubnormalF32),
-                                                          -kSubnormalF32)  //
-                                     /* Types<f16V, AFloatM>(1.0_a), */    //
-                                     )));
+    MaterializeAbstractNumericToConcreteType,
+    testing::Combine(testing::Values(Expectation::kMaterialize),
+                     testing::ValuesIn(kMatrixMethods),
+                     testing::ValuesIn(std::vector<Data>{
+                         Types<f32M, AFloatM>(0.0_a, 0.0),                              //
+                         Types<f32M, AFloatM>(1.0_a, 1.0),                              //
+                         Types<f32M, AFloatM>(-1.0_a, -1.0),                            //
+                         Types<f32M, AFloatM>(AFloat(kHighestF32), kHighestF32),        //
+                         Types<f32M, AFloatM>(AFloat(kLowestF32), kLowestF32),          //
+                         Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64),                  //
+                         Types<f32M, AFloatM>(AFloat(kSubnormalF32), kSubnormalF32),    //
+                         Types<f32M, AFloatM>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
+                         /* Types<f16V, AFloatM>(1.0_a), */                             //
+                     })));
 
-INSTANTIATE_TEST_SUITE_P(
-    MaterializeSwitch,
-    MaterializeAbstractNumeric,                                                       //
-    testing::Combine(testing::Values(Expectation::kMaterialize),                      //
-                     testing::ValuesIn(kSwitchMethods),                               //
-                     testing::Values(Types<i32, AInt>(0_a, 0.0),                      //
-                                     Types<i32, AInt>(2147483647_a, 2147483647.0),    //
-                                     Types<i32, AInt>(-2147483648_a, -2147483648.0),  //
-                                     Types<u32, AInt>(0_a, 0.0),                      //
-                                     Types<u32, AInt>(4294967295_a, 4294967295.0))));
+INSTANTIATE_TEST_SUITE_P(MaterializeSwitch,
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kMaterialize),
+                                          testing::ValuesIn(kSwitchMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32, AInt>(0_a, 0.0),                        //
+                                              Types<i32, AInt>(1_a, 1.0),                        //
+                                              Types<i32, AInt>(-1_a, -1.0),                      //
+                                              Types<i32, AInt>(AInt(kHighestI32), kHighestI32),  //
+                                              Types<i32, AInt>(AInt(kLowestI32), kLowestI32),    //
+                                              Types<u32, AInt>(0_a, 0.0),                        //
+                                              Types<u32, AInt>(1_a, 1.0),                        //
+                                              Types<u32, AInt>(AInt(kHighestU32), kHighestU32),  //
+                                              Types<u32, AInt>(AInt(kLowestU32), kLowestU32),    //
+                                          })));
 
 // 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>(),           //
-                                                          Types<AFloat, AFloat>(),       //
-                                                          Types<AIntV, AIntV>(),         //
-                                                          Types<AFloatV, AFloatV>(),     //
-                                                          Types<AFloatM, AFloatM>())));
-INSTANTIATE_TEST_SUITE_P(InvalidCast,
-                         MaterializeAbstractNumeric,                                   //
-                         testing::Combine(testing::Values(Expectation::kInvalidCast),  //
-                                          testing::ValuesIn(kScalarMethods),           //
-                                          testing::Values(Types<i32, AFloat>(),        //
-                                                          Types<u32, AFloat>(),        //
-                                                          Types<i32V, AFloatV>(),      //
-                                                          Types<u32V, AFloatV>())));
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kNoMaterialize),
+                                          testing::Values(Method::kBuiltinArg, Method::kBinaryOp),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<AInt, AInt>(),        //
+                                              Types<AFloat, AFloat>(),    //
+                                              Types<AIntV, AIntV>(),      //
+                                              Types<AFloatV, AFloatV>(),  //
+                                              Types<AFloatM, AFloatM>(),  //
+                                          })));
+INSTANTIATE_TEST_SUITE_P(InvalidConversion,
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kInvalidConversion),
+                                          testing::ValuesIn(kScalarMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32, AFloat>(),    //
+                                              Types<u32, AFloat>(),    //
+                                              Types<i32V, AFloatV>(),  //
+                                              Types<u32V, AFloatV>(),  //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(ScalarValueCannotBeRepresented,
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kScalarMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32, AInt>(0_a, kHighestI32 + 1),  //
+                                              Types<i32, AInt>(0_a, kLowestI32 - 1),   //
+                                              Types<u32, AInt>(0_a, kHighestU32 + 1),  //
+                                              Types<u32, AInt>(0_a, kLowestU32 - 1),   //
+                                              Types<f32, AFloat>(0.0_a, kTooBigF32),   //
+                                              Types<f32, AFloat>(0.0_a, -kTooBigF32),  //
+                                              /* Types<f16, AFloat>(), */              //
+                                              /* Types<f16, AFloat>(), */              //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(VectorValueCannotBeRepresented,
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kVectorMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32V, AIntV>(0_a, kHighestI32 + 1),  //
+                                              Types<i32V, AIntV>(0_a, kLowestI32 - 1),   //
+                                              Types<u32V, AIntV>(0_a, kHighestU32 + 1),  //
+                                              Types<u32V, AIntV>(0_a, kLowestU32 - 1),   //
+                                              Types<f32V, AFloatV>(0.0_a, kTooBigF32),   //
+                                              Types<f32V, AFloatV>(0.0_a, -kTooBigF32),  //
+                                              /* Types<f16V, AFloatV>(), */              //
+                                              /* Types<f16V, AFloatV>(), */              //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(MatrixValueCannotBeRepresented,
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kMatrixMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<f32M, AFloatM>(0.0_a, kTooBigF32),   //
+                                              Types<f32M, AFloatM>(0.0_a, -kTooBigF32),  //
+                                              /* Types<f16M, AFloatM>(), */              //
+                                              /* Types<f16M, AFloatM>(), */              //
+                                          })));
+
+}  // namespace materialize_abstract_numeric_to_concrete_type
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Tests that in the absence of a 'target type' an abstract-int will materialize to i32, and an
+// abstract-float will materialize to f32.
+////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace materialize_abstract_numeric_to_default_type {
+
+// How should the materialization occur?
+enum class Method {
+    // var a = abstract_expr;
+    kVar,
+
+    // let a = abstract_expr;
+    kLet,
+
+    // min(abstract_expr, abstract_expr);
+    kBuiltinArg,
+
+    // array<i32, abstract_expr>();
+    kArrayLength,
+
+    // switch (abstract_expr) {
+    //   case abstract_expr: {}
+    //   default: {}
+    // }
+    kSwitch,
+};
+
+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::kBuiltinArg:
+            return o << "builtin-arg";
+        case Method::kArrayLength:
+            return o << "array-length";
+        case Method::kSwitch:
+            return o << "switch";
+    }
+    return o << "<unknown>";
+}
+
+struct Data {
+    std::string expected_type_name;
+    std::string expected_element_type_name;
+    builder::sem_type_func_ptr expected_sem_ty;
+    std::string abstract_type_name;
+    builder::ast_expr_func_ptr abstract_expr;
+    std::variant<AInt, AFloat> materialized_value;
+    double literal_value;
+};
+
+template <typename EXPECTED_TYPE, typename ABSTRACT_TYPE, typename MATERIALIZED_TYPE>
+Data Types(MATERIALIZED_TYPE materialized_value, double literal_value) {
+    using ExpectedDataType = builder::DataType<EXPECTED_TYPE>;
+    using AbstractDataType = builder::DataType<ABSTRACT_TYPE>;
+    using TargetElementDataType = builder::DataType<typename ExpectedDataType::ElementType>;
+    return {
+        ExpectedDataType::Name(),       // expected_type_name
+        TargetElementDataType::Name(),  // expected_element_type_name
+        ExpectedDataType::Sem,          // expected_sem_ty
+        AbstractDataType::Name(),       // abstract_type_name
+        AbstractDataType::Expr,         // abstract_expr
+        materialized_value,
+        literal_value,
+    };
+}
+
+static std::ostream& operator<<(std::ostream& o, const Data& c) {
+    auto print_value = [&](auto&& v) { o << v; };
+    o << "[" << c.expected_type_name << " <- " << c.abstract_type_name << "] [";
+    std::visit(print_value, c.materialized_value);
+    o << " <- " << c.literal_value << "]";
+    return o;
+}
+
+using MaterializeAbstractNumericToDefaultType =
+    resolver::ResolverTestWithParam<std::tuple<Expectation, Method, Data>>;
+
+TEST_P(MaterializeAbstractNumericToDefaultType, 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);
+
+    ast::ExpressionList abstract_exprs;
+    auto abstract_expr = [&] {
+        auto* expr = data.abstract_expr(*this, data.literal_value);
+        abstract_exprs.emplace_back(expr);
+        return expr;
+    };
+    switch (method) {
+        case Method::kVar:
+            WrapInFunction(Decl(Var("a", nullptr, abstract_expr())));
+            break;
+        case Method::kLet:
+            WrapInFunction(Decl(Let("a", nullptr, abstract_expr())));
+            break;
+        case Method::kBuiltinArg:
+            WrapInFunction(CallStmt(Call("min", abstract_expr(), abstract_expr())));
+            break;
+        case Method::kArrayLength:
+            WrapInFunction(Construct(ty.array(ty.i32(), abstract_expr())));
+            break;
+        case Method::kSwitch:
+            WrapInFunction(Switch(abstract_expr(),
+                                  Case(abstract_expr()->As<ast::IntLiteralExpression>()),
+                                  DefaultCase()));
+            break;
+    }
+
+    auto check_types_and_values = [&](const sem::Expression* expr) {
+        auto* expected_sem_ty = data.expected_sem_ty(*this);
+
+        EXPECT_TYPE(expr->Type(), expected_sem_ty);
+        EXPECT_TYPE(expr->ConstantValue().Type(), expected_sem_ty);
+
+        uint32_t num_elems = 0;
+        const sem::Type* expected_sem_el_ty = sem::Type::ElementOf(expected_sem_ty, &num_elems);
+        EXPECT_TYPE(expr->ConstantValue().ElementType(), expected_sem_el_ty);
+        expr->ConstantValue().WithElements([&](auto&& vec) {
+            using VEC_TY = std::decay_t<decltype(vec)>;
+            using EL_TY = typename VEC_TY::value_type;
+            ASSERT_TRUE(std::holds_alternative<EL_TY>(data.materialized_value));
+            VEC_TY expected(num_elems, std::get<EL_TY>(data.materialized_value));
+            EXPECT_EQ(vec, expected);
+        });
+    };
+
+    switch (expectation) {
+        case Expectation::kMaterialize: {
+            ASSERT_TRUE(r()->Resolve()) << r()->error();
+            for (auto* expr : abstract_exprs) {
+                auto* materialize = Sem().Get<sem::Materialize>(expr);
+                ASSERT_NE(materialize, nullptr);
+                check_types_and_values(materialize);
+            }
+            break;
+        }
+        case Expectation::kInvalidConversion: {
+            ASSERT_FALSE(r()->Resolve());
+            std::string expect;
+            switch (method) {
+                case Method::kBuiltinArg:
+                    expect = "error: no matching call to min(" + data.abstract_type_name + ", " +
+                             data.abstract_type_name + ")";
+                    break;
+                default:
+                    expect = "error: cannot convert value of type '" + data.abstract_type_name +
+                             "' to type '" + data.expected_type_name + "'";
+                    break;
+            }
+            EXPECT_THAT(r()->error(), testing::StartsWith(expect));
+            break;
+        }
+        case Expectation::kValueCannotBeRepresented:
+            ASSERT_FALSE(r()->Resolve());
+            EXPECT_THAT(r()->error(), testing::HasSubstr("cannot be represented as '" +
+                                                         data.expected_element_type_name + "'"));
+            break;
+        default:
+            FAIL() << "unhandled expectation: " << expectation;
+    }
+}
+
+/// Methods that support scalar materialization
+constexpr Method kScalarMethods[] = {
+    Method::kLet,
+    Method::kVar,
+    Method::kBuiltinArg,
+};
+
+/// Methods that support vector materialization
+constexpr Method kVectorMethods[] = {
+    Method::kLet,
+    Method::kVar,
+    Method::kBuiltinArg,
+};
+
+/// Methods that support matrix materialization
+constexpr Method kMatrixMethods[] = {
+    Method::kLet,
+    Method::kVar,
+};
+
+/// Methods that support materialization for switch cases
+constexpr Method kSwitchMethods[] = {
+    Method::kSwitch,
+};
 
 INSTANTIATE_TEST_SUITE_P(
-    ScalarValueCannotBeRepresented,
-    MaterializeAbstractNumeric,                                                //
-    testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),  //
-                     testing::ValuesIn(kScalarMethods),                        //
-                     testing::Values(Types<i32, AInt>(0_a, 2147483648.0),      //
-                                     Types<i32, AInt>(0_a, -2147483649.0),     //
-                                     Types<u32, AInt>(0_a, 4294967296),        //
-                                     Types<u32, AInt>(0_a, -1.0),              //
-                                     Types<f32, AFloat>(0.0_a, 3.5e+38),       //
-                                     Types<f32, AFloat>(0.0_a, -3.5e+38)       //
-                                     /* Types<f16, AFloat>(), */               //
-                                     /* Types<f16, AFloat>(), */)));
+    MaterializeScalar,
+    MaterializeAbstractNumericToDefaultType,
+    testing::Combine(testing::Values(Expectation::kMaterialize),
+                     testing::ValuesIn(kScalarMethods),
+                     testing::ValuesIn(std::vector<Data>{
+                         Types<i32, AInt>(0_a, 0.0),                                  //
+                         Types<i32, AInt>(1_a, 1.0),                                  //
+                         Types<i32, AInt>(-1_a, -1.0),                                //
+                         Types<i32, AInt>(AInt(kHighestI32), kHighestI32),            //
+                         Types<i32, AInt>(AInt(kLowestI32), kLowestI32),              //
+                         Types<f32, AFloat>(0.0_a, 0.0),                              //
+                         Types<f32, AFloat>(AFloat(kHighestF32), kHighestF32),        //
+                         Types<f32, AFloat>(AFloat(kLowestF32), kLowestF32),          //
+                         Types<f32, AFloat>(AFloat(kPiF32), kPiF64),                  //
+                         Types<f32, AFloat>(AFloat(kSubnormalF32), kSubnormalF32),    //
+                         Types<f32, AFloat>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
+                     })));
 
 INSTANTIATE_TEST_SUITE_P(
-    VectorValueCannotBeRepresented,
-    MaterializeAbstractNumeric,                                                //
-    testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),  //
-                     testing::ValuesIn(kVectorMethods),                        //
-                     testing::Values(Types<i32V, AIntV>(0_a, 2147483648.0),    //
-                                     Types<i32V, AIntV>(0_a, -2147483649.0),   //
-                                     Types<u32V, AIntV>(0_a, 4294967296),      //
-                                     Types<u32V, AIntV>(0_a, -1.0),            //
-                                     Types<f32V, AFloatV>(0.0_a, 3.5e+38),     //
-                                     Types<f32V, AFloatV>(0.0_a, -3.5e+38)     //
-                                     /* Types<f16V, AFloatV>(), */             //
-                                     /* Types<f16V, AFloatV>(), */)));
+    MaterializeVector,
+    MaterializeAbstractNumericToDefaultType,
+    testing::Combine(testing::Values(Expectation::kMaterialize),
+                     testing::ValuesIn(kVectorMethods),
+                     testing::ValuesIn(std::vector<Data>{
+                         Types<i32V, AIntV>(0_a, 0.0),                                  //
+                         Types<i32V, AIntV>(1_a, 1.0),                                  //
+                         Types<i32V, AIntV>(-1_a, -1.0),                                //
+                         Types<i32V, AIntV>(AInt(kHighestI32), kHighestI32),            //
+                         Types<i32V, AIntV>(AInt(kLowestI32), kLowestI32),              //
+                         Types<f32V, AFloatV>(0.0_a, 0.0),                              //
+                         Types<f32V, AFloatV>(1.0_a, 1.0),                              //
+                         Types<f32V, AFloatV>(-1.0_a, -1.0),                            //
+                         Types<f32V, AFloatV>(AFloat(kHighestF32), kHighestF32),        //
+                         Types<f32V, AFloatV>(AFloat(kLowestF32), kLowestF32),          //
+                         Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64),                  //
+                         Types<f32V, AFloatV>(AFloat(kSubnormalF32), kSubnormalF32),    //
+                         Types<f32V, AFloatV>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
+                     })));
 
 INSTANTIATE_TEST_SUITE_P(
-    MatrixValueCannotBeRepresented,
-    MaterializeAbstractNumeric,                                                //
-    testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),  //
-                     testing::ValuesIn(kMatrixMethods),                        //
-                     testing::Values(Types<f32M, AFloatM>(0.0_a, 3.5e+38),     //
-                                     Types<f32M, AFloatM>(0.0_a, -3.5e+38)     //
-                                     /* Types<f16M, AFloatM>(), */             //
-                                     /* Types<f16M, AFloatM>(), */)));
+    MaterializeMatrix,
+    MaterializeAbstractNumericToDefaultType,
+    testing::Combine(testing::Values(Expectation::kMaterialize),
+                     testing::ValuesIn(kMatrixMethods),
+                     testing::ValuesIn(std::vector<Data>{
+                         Types<f32M, AFloatM>(0.0_a, 0.0),                              //
+                         Types<f32M, AFloatM>(1.0_a, 1.0),                              //
+                         Types<f32M, AFloatM>(-1.0_a, -1.0),                            //
+                         Types<f32M, AFloatM>(AFloat(kHighestF32), kHighestF32),        //
+                         Types<f32M, AFloatM>(AFloat(kLowestF32), kLowestF32),          //
+                         Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64),                  //
+                         Types<f32M, AFloatM>(AFloat(kSubnormalF32), kSubnormalF32),    //
+                         Types<f32M, AFloatM>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
+                     })));
 
-}  // namespace MaterializeTests
+INSTANTIATE_TEST_SUITE_P(MaterializeSwitch,
+                         MaterializeAbstractNumericToDefaultType,
+                         testing::Combine(testing::Values(Expectation::kMaterialize),
+                                          testing::ValuesIn(kSwitchMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32, AInt>(0_a, 0.0),                        //
+                                              Types<i32, AInt>(AInt(kHighestI32), kHighestI32),  //
+                                              Types<i32, AInt>(AInt(kLowestI32), kLowestI32),    //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(ScalarValueCannotBeRepresented,
+                         MaterializeAbstractNumericToDefaultType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kScalarMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32, AInt>(0_a, kHighestI32 + 1),  //
+                                              Types<i32, AInt>(0_a, kLowestI32 - 1),   //
+                                              Types<f32, AFloat>(0.0_a, kTooBigF32),   //
+                                              Types<f32, AFloat>(0.0_a, -kTooBigF32),  //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(VectorValueCannotBeRepresented,
+                         MaterializeAbstractNumericToDefaultType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kVectorMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32V, AIntV>(0_a, kHighestI32 + 1),  //
+                                              Types<i32V, AIntV>(0_a, kLowestI32 - 1),   //
+                                              Types<i32V, AIntV>(0_a, kHighestU32 + 1),  //
+                                              Types<f32V, AFloatV>(0.0_a, kTooBigF32),   //
+                                              Types<f32V, AFloatV>(0.0_a, -kTooBigF32),  //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(MatrixValueCannotBeRepresented,
+                         MaterializeAbstractNumericToDefaultType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kMatrixMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<f32M, AFloatM>(0.0_a, kTooBigF32),   //
+                                              Types<f32M, AFloatM>(0.0_a, -kTooBigF32),  //
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(SwitchValueCannotBeRepresented,
+                         MaterializeAbstractNumericToDefaultType,
+                         testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
+                                          testing::ValuesIn(kSwitchMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<i32, AInt>(0_a, kHighestI32 + 1),  //
+                                              Types<i32, AInt>(0_a, kLowestI32 - 1),   //
+                                          })));
+
+}  // namespace materialize_abstract_numeric_to_default_type
 
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index af8f16a..9fdb576 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -1145,7 +1145,7 @@
     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);
+        return builder_->create<sem::Matrix>(f32v(rows), columns);
     };
 
     // Type dispatch based on the expression type