[ir] Validate construct for structure types Check the number and type of arguments matches the structure members. Change-Id: I61d4e16676a44edba01e9b31f3b55b64f8b3a465 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/191661 Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc index 512ccc6..d7f00f1 100644 --- a/src/tint/lang/core/ir/validator.cc +++ b/src/tint/lang/core/ir/validator.cc
@@ -338,6 +338,10 @@ /// @param call the call to validate void CheckBuiltinCall(const BuiltinCall* call); + /// Validates the given construct + /// @param construct the construct to validate + void CheckConstruct(const Construct* construct); + /// Validates the given user call /// @param call the call to validate void CheckUserCall(const UserCall* call); @@ -971,7 +975,7 @@ call, // [&](const Bitcast*) {}, // [&](const BuiltinCall* c) { CheckBuiltinCall(c); }, // - [&](const Construct*) {}, // + [&](const Construct* c) { CheckConstruct(c); }, // [&](const Convert*) {}, // [&](const Discard*) {}, // [&](const UserCall* c) { CheckUserCall(c); }, // @@ -1000,6 +1004,32 @@ } } +void Validator::CheckConstruct(const Construct* construct) { + auto args = construct->Args(); + if (args.IsEmpty()) { + // Zero-value constructors are valid for all constructible types. + return; + } + + if (auto* str = construct->Result(0)->Type()->As<type::Struct>()) { + auto members = str->Members(); + if (args.Length() != str->Members().Length()) { + AddError(construct) << "structure has " << members.Length() + << " members, but construct provides " << args.Length() + << " arguments"; + return; + } + for (size_t i = 0; i < args.Length(); i++) { + if (args[i] && args[i]->Type() != members[i]->Type()) { + AddError(construct, Construct::kArgsOperandOffset + i) + << "sructure member " << i << " is of type " + << style::Type(members[i]->Type()->FriendlyName()) + << ", but argument is of type " << style::Type(args[i]->Type()->FriendlyName()); + } + } + } +} + void Validator::CheckUserCall(const UserCall* call) { if (call->Target()->Stage() != Function::PipelineStage::kUndefined) { AddError(call, UserCall::kFunctionOperandOffset)
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc index da74d0c..aaba721 100644 --- a/src/tint/lang/core/ir/validator_test.cc +++ b/src/tint/lang/core/ir/validator_test.cc
@@ -30,13 +30,14 @@ #include <utility> #include "gmock/gmock.h" +#include "gtest/gtest.h" #include "src/tint/lang/core/address_space.h" #include "src/tint/lang/core/ir/builder.h" #include "src/tint/lang/core/ir/function_param.h" #include "src/tint/lang/core/ir/ir_helper_test.h" #include "src/tint/lang/core/ir/validator.h" -#include "src/tint/lang/core/type/array.h" +#include "src/tint/lang/core/number.h" #include "src/tint/lang/core/type/manager.h" #include "src/tint/lang/core/type/matrix.h" #include "src/tint/lang/core/type/memory_view.h" @@ -462,6 +463,153 @@ )"); } +TEST_F(IR_ValidatorTest, Construct_Struct_ZeroValue) { + auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("a"), ty.i32()}, + {mod.symbols.New("b"), ty.u32()}, + }); + + auto* f = b.Function("f", ty.void_()); + b.Append(f->Block(), [&] { + b.Construct(str_ty); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_EQ(res, Success) << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Construct_Struct_ValidArgs) { + auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("a"), ty.i32()}, + {mod.symbols.New("b"), ty.u32()}, + }); + + auto* f = b.Function("f", ty.void_()); + b.Append(f->Block(), [&] { + b.Construct(str_ty, 1_i, 2_u); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_EQ(res, Success) << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Construct_Struct_NotEnoughArgs) { + auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("a"), ty.i32()}, + {mod.symbols.New("b"), ty.u32()}, + }); + + auto* f = b.Function("f", ty.void_()); + b.Append(f->Block(), [&] { + b.Construct(str_ty, 1_i); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_EQ(res.Failure().reason.Str(), + R"(:8:19 error: construct: structure has 2 members, but construct provides 1 arguments + %2:MyStruct = construct 1i + ^^^^^^^^^ + +:7:3 note: in block + $B1: { + ^^^ + +note: # Disassembly +MyStruct = struct @align(4) { + a:i32 @offset(0) + b:u32 @offset(4) +} + +%f = func():void { + $B1: { + %2:MyStruct = construct 1i + ret + } +} +)"); +} + +TEST_F(IR_ValidatorTest, Construct_Struct_TooManyArgs) { + auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("a"), ty.i32()}, + {mod.symbols.New("b"), ty.u32()}, + }); + + auto* f = b.Function("f", ty.void_()); + b.Append(f->Block(), [&] { + b.Construct(str_ty, 1_i, 2_u, 3_i); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_EQ(res.Failure().reason.Str(), + R"(:8:19 error: construct: structure has 2 members, but construct provides 3 arguments + %2:MyStruct = construct 1i, 2u, 3i + ^^^^^^^^^ + +:7:3 note: in block + $B1: { + ^^^ + +note: # Disassembly +MyStruct = struct @align(4) { + a:i32 @offset(0) + b:u32 @offset(4) +} + +%f = func():void { + $B1: { + %2:MyStruct = construct 1i, 2u, 3i + ret + } +} +)"); +} + +TEST_F(IR_ValidatorTest, Construct_Struct_WrongArgType) { + auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("a"), ty.i32()}, + {mod.symbols.New("b"), ty.u32()}, + }); + + auto* f = b.Function("f", ty.void_()); + b.Append(f->Block(), [&] { + b.Construct(str_ty, 1_i, 2_i); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_EQ( + res.Failure().reason.Str(), + R"(:8:33 error: construct: sructure member 1 is of type 'u32', but argument is of type 'i32' + %2:MyStruct = construct 1i, 2i + ^^ + +:7:3 note: in block + $B1: { + ^^^ + +note: # Disassembly +MyStruct = struct @align(4) { + a:i32 @offset(0) + b:u32 @offset(4) +} + +%f = func():void { + $B1: { + %2:MyStruct = construct 1i, 2i + ret + } +} +)"); +} + TEST_F(IR_ValidatorTest, Block_NoTerminator) { b.Function("my_func", ty.void_());