[tint][ir][val] Check that conversion for specific types exists

This CL introduces looking up conversion entries in the intrinsic table
for the convert call. It does not enforce that entry is a converter (not
a constructor), so is less restrictive then the intended final state.

A follow up CL will add the 'is converter' check and update various
tests.

Bug: 389589696
Change-Id: I2726e6546545fa5cb719343f04e3c286290b727a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/222115
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 9458b1d..d1ff4fe 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -2878,7 +2878,45 @@
 }
 
 void Validator::CheckConvert(const Convert* convert) {
-    CheckResultsAndOperands(convert, Convert::kNumResults, Convert::kNumOperands);
+    if (!CheckResultsAndOperands(convert, Convert::kNumResults, Convert::kNumOperands)) {
+        return;
+    }
+
+    auto* result_type = convert->Result(0)->Type();
+    auto* value_type = convert->Operand(Convert::kValueOperandOffset)->Type();
+
+    intrinsic::CtorConv conv_ty;
+    Vector<const core::type::Type*, 1> template_type;
+    tint::Switch(
+        result_type,                                                             //
+        [&](const core::type::I32*) { conv_ty = intrinsic::CtorConv::kI32; },    //
+        [&](const core::type::U32*) { conv_ty = intrinsic::CtorConv::kU32; },    //
+        [&](const core::type::F32*) { conv_ty = intrinsic::CtorConv::kF32; },    //
+        [&](const core::type::F16*) { conv_ty = intrinsic::CtorConv::kF16; },    //
+        [&](const core::type::Bool*) { conv_ty = intrinsic::CtorConv::kBool; },  //
+        [&](const core::type::Vector* v) {
+            conv_ty = intrinsic::VectorCtorConv(v->Width());
+            template_type.Push(v->Type());
+        },
+        [&](const core::type::Matrix* m) {
+            conv_ty = intrinsic::MatrixCtorConv(m->Columns(), m->Rows());
+            template_type.Push(m->Type());
+        },
+        [&](Default) { conv_ty = intrinsic::CtorConv::kNone; });
+
+    if (conv_ty == intrinsic::CtorConv::kNone) {
+        AddError(convert) << "not defined for result type, " << StyledText{}
+                          << style::Type(result_type->FriendlyName());
+        return;
+    }
+
+    auto table = intrinsic::Table<intrinsic::Dialect>(type_mgr_, symbols_);
+    auto match =
+        table.Lookup(conv_ty, template_type, Vector{value_type}, core::EvaluationStage::kOverride);
+    if (match != Success) {
+        AddError(convert) << match.Failure();
+        return;
+    }
 }
 
 void Validator::CheckDiscard(const tint::core::ir::Discard* discard) {
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 0f5e595..8bfeca0 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -627,6 +627,236 @@
 )");
 }
 
+TEST_F(IR_ValidatorTest, Convert_ScalarToScalar) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.f32());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.i32(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_ScalarToVec) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.f32());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.vec2<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    // There is a vector constructor that fills all elements with the scalar,
+    // this will start failing when checking for kConvertor is added.
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_ScalarToMat) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.f32());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.mat2x3<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(":3:22 error: convert: no matching constructor for 'mat2x3<f32>(f32)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToVec) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.vec2<u32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.vec2<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToVec_WidthMismatch) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.vec2<u32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.vec4<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(res.Failure().reason.Str(),
+                testing::HasSubstr(
+                    ":3:20 error: convert: no matching constructor for 'vec4<f32>(vec2<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToScalar) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.vec2<u32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.f32(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(":3:14 error: convert: no matching constructor for 'f32(vec2<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToMat) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.vec2<u32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.mat3x2<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(res.Failure().reason.Str(),
+                testing::HasSubstr(
+                    ":3:22 error: convert: no matching constructor for 'mat3x2<f32>(vec2<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToMat) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.mat4x4<f32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.mat4x4<f16>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToMat_ShapeMismatch) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.mat4x4<f32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.mat2x2<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(
+            ":3:22 error: convert: no matching constructor for 'mat2x2<f32>(mat4x4<f32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToScalar) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.mat4x4<f32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.f32(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(":3:14 error: convert: no matching constructor for 'f32(mat4x4<f32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToVec) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.mat4x4<u32>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.vec4<f32>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(res.Failure().reason.Str(),
+                testing::HasSubstr(
+                    ":3:20 error: convert: no matching constructor for 'vec4<f32>(mat4x4<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_4xU8ToU32) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.vec4<u8>());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.u32(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod, Capabilities{Capability::kAllow8BitIntegers});
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(":3:14 error: convert: no matching constructor for 'u32(vec4<u8>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_U32To4xU8) {
+    auto* f = b.Function("f", ty.void_());
+    auto* p = b.FunctionParam("p", ty.u32());
+    f->AppendParam(p);
+    b.Append(f->Block(), [&] {
+        b.Convert(ty.vec4<u8>(), p);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod, Capabilities{Capability::kAllow8BitIntegers});
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(":3:19 error: convert: no matching constructor for 'vec4<u8>(u32)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_PtrToVal) {
+    auto* f = b.Function("f", ty.void_());
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var("v", 0_u);
+        b.Convert(ty.u32(), v->Result(0));
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(res.Failure().reason.Str(),
+                testing::HasSubstr(":4:14 error: convert: no matching constructor for "
+                                   "'u32(ptr<function, u32, read_write>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_PtrToPtr) {
+    auto* f = b.Function("f", ty.void_());
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var("v", 0_u);
+        b.Convert(v->Result(0)->Type(), v->Result(0));
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason.Str(),
+        testing::HasSubstr(
+            ":4:41 error: convert: not defined for result type, 'ptr<function, u32, read_write>'"));
+}
+
 TEST_F(IR_ValidatorTest, Block_NoTerminator) {
     b.Function("my_func", ty.void_());
 
diff --git a/src/tint/lang/core/ir/validator_type_test.cc b/src/tint/lang/core/ir/validator_type_test.cc
index ff9bde3..c1cc6f9 100644
--- a/src/tint/lang/core/ir/validator_type_test.cc
+++ b/src/tint/lang/core/ir/validator_type_test.cc
@@ -748,16 +748,16 @@
 TEST_F(IR_ValidatorTest, Int8Type_InstructionOperand_NotAllowed) {
     auto* fn = b.Function("my_func", ty.void_());
     b.Append(fn->Block(), [&] {
-        b.Convert(ty.i32(), u8(1));
+        b.Let("l", u8(1));
         b.Return(fn);
     });
 
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:3:22 error: convert: 8-bit integer types are not permitted
-    %2:i32 = convert 1u8
-                     ^^^
+              R"(:3:5 error: let: 8-bit integer types are not permitted
+    %l:u8 = let 1u8
+    ^^^^^
 
 :2:3 note: in block
   $B1: {
@@ -766,7 +766,7 @@
 note: # Disassembly
 %my_func = func():void {
   $B1: {
-    %2:i32 = convert 1u8
+    %l:u8 = let 1u8
     ret
   }
 }
@@ -776,7 +776,7 @@
 TEST_F(IR_ValidatorTest, Int8Type_InstructionOperand_Allowed) {
     auto* fn = b.Function("my_func", ty.void_());
     b.Append(fn->Block(), [&] {
-        b.Convert(ty.i32(), u8(1));
+        b.Let("l", u8(1));
         b.Return(fn);
     });
 
diff --git a/src/tint/lang/glsl/writer/convert_test.cc b/src/tint/lang/glsl/writer/convert_test.cc
index b3e6df6..c438070 100644
--- a/src/tint/lang/glsl/writer/convert_test.cc
+++ b/src/tint/lang/glsl/writer/convert_test.cc
@@ -36,7 +36,7 @@
 TEST_F(GlslWriterTest, ConvertU32) {
     auto* f = b.Function("a", ty.u32());
     b.Append(f->Block(), [&] {
-        auto* v = b.Var("v", 2_i);
+        auto* v = b.Let("v", 2_i);
         b.Return(f, b.Convert(ty.u32(), v));
     });
 
diff --git a/src/tint/lang/hlsl/writer/convert_test.cc b/src/tint/lang/hlsl/writer/convert_test.cc
index 525fc33..df9a508 100644
--- a/src/tint/lang/hlsl/writer/convert_test.cc
+++ b/src/tint/lang/hlsl/writer/convert_test.cc
@@ -36,7 +36,7 @@
 TEST_F(HlslWriterTest, ConvertU32) {
     auto* f = b.Function("a", ty.u32());
     b.Append(f->Block(), [&] {
-        auto* v = b.Var("v", 2_i);
+        auto* v = b.Let("v", 2_i);
         b.Return(f, b.Convert(ty.u32(), v));
     });