[tint][ir][val] Improve checks on Returns
- Adds testing for operands and results expected for the instruction
- Refactors error logging to use NameOf to avoid accidental segfaults
- Fixes a couple of potential bad patterns in other implementations of
NameOf
- Fixes a typo in swizzle.h
Fixes: 360058656
Change-Id: I87dbbb02e7dbf2a6203662b1ecc2c7014e460963
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/202500
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/return.h b/src/tint/lang/core/ir/return.h
index 9faa020..fa474bf 100644
--- a/src/tint/lang/core/ir/return.h
+++ b/src/tint/lang/core/ir/return.h
@@ -49,6 +49,15 @@
/// The offset in Operands() for the return argument
static constexpr size_t kArgsOperandOffset = 1;
+ /// The fixed number of results returned by return instructions
+ static constexpr size_t kNumResults = 0;
+
+ /// The minimum number of operands accepted by return instructions
+ static constexpr size_t kMinOperands = 1;
+
+ /// The maximum number of operands accepted by return instructions
+ static constexpr size_t kMaxOperands = 2;
+
/// Constructor (no operands)
/// @param id the instruction id
explicit Return(Id id);
diff --git a/src/tint/lang/core/ir/swizzle.h b/src/tint/lang/core/ir/swizzle.h
index 0b32ffd..b29db45 100644
--- a/src/tint/lang/core/ir/swizzle.h
+++ b/src/tint/lang/core/ir/swizzle.h
@@ -46,7 +46,7 @@
static constexpr size_t kNumResults = 1;
/// The fixed number of operands expected for swizzle instructions
- /// @note indices for swizzle are handled seperately from the operands, so not included here
+ /// @note indices for swizzle are handled separately from the operands, so not included here
static constexpr size_t kNumOperands = 1;
/// Minimum number of indices expected for swizzle instructions
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index c6cfa17..f7b5af1 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -263,10 +263,14 @@
/// @param res the res
void AddDeclarationNote(const InstructionResult* res);
- /// @param decl the value, instruction or block to get the name for
+ /// @param decl the type, value, instruction or block to get the name for
/// @returns the styled name for the given value, instruction or block
StyledText NameOf(const CastableBase* decl);
+ // @param ty the type to get the name for
+ /// @returns the styled name for the given type
+ StyledText NameOf(const type::Type* ty);
+
/// @param v the value to get the name for
/// @returns the styled name for the given value
StyledText NameOf(const Value* v);
@@ -841,22 +845,30 @@
StyledText Validator::NameOf(const CastableBase* decl) {
return tint::Switch(
decl, //
+ [&](const type::Type* ty) { return NameOf(ty); },
[&](const Value* value) { return NameOf(value); },
[&](const Instruction* inst) { return NameOf(inst); },
[&](const Block* block) { return NameOf(block); }, //
TINT_ICE_ON_NO_MATCH);
}
+StyledText Validator::NameOf(const type::Type* ty) {
+ auto name = ty ? ty->FriendlyName() : "undef";
+ return StyledText{} << style::Type(name);
+}
+
StyledText Validator::NameOf(const Value* value) {
return Disassemble().NameOf(value);
}
StyledText Validator::NameOf(const Instruction* inst) {
- return StyledText{} << style::Instruction(inst->FriendlyName());
+ auto name = inst ? inst->FriendlyName() : "undef";
+ return StyledText{} << style::Instruction(name);
}
StyledText Validator::NameOf(const Block* block) {
- return StyledText{} << style::Instruction(block->Parent()->FriendlyName()) << " block "
+ auto parent_name = block->Parent() ? block->Parent()->FriendlyName() : "undef";
+ return StyledText{} << style::Instruction(parent_name) << " block "
<< Disassemble().NameOf(block);
}
@@ -1967,11 +1979,19 @@
}
void Validator::CheckReturn(const Return* ret) {
- auto* func = ret->Func();
- if (func == nullptr) {
- AddError(ret) << "undefined function";
+ if (!CheckResultsAndOperandRange(ret, Return::kNumResults, Return::kMinOperands,
+ Return::kMaxOperands)) {
return;
}
+
+ auto* func = ret->Func();
+ if (func == nullptr) {
+ // Func() returning nullptr after CheckResultsAndOperandRange is due to the first operand
+ // being not a function
+ AddError(ret) << "expected function for first operand";
+ return;
+ }
+
if (func->ReturnType()->Is<core::type::Void>()) {
if (ret->Value()) {
AddError(ret) << "unexpected return value";
@@ -1980,10 +2000,8 @@
if (!ret->Value()) {
AddError(ret) << "expected return value";
} else if (ret->Value()->Type() != func->ReturnType()) {
- AddError(ret) << "return value type "
- << style::Type(ret->Value()->Type()->FriendlyName())
- << " does not match function return type "
- << style::Type(func->ReturnType()->FriendlyName());
+ AddError(ret) << "return value type " << NameOf(ret->Value()->Type())
+ << " does not match function return type " << NameOf(func->ReturnType());
}
}
}
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 678291c..4115311 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -5993,15 +5993,71 @@
EXPECT_EQ(ir::Validate(mod), Success);
}
-TEST_F(IR_ValidatorTest, Return_NullFunction) {
- auto* f = b.Function("my_func", ty.void_());
+TEST_F(IR_ValidatorTest, Return_UnexpectedResult) {
+ auto* f = b.Function("my_func", ty.i32());
b.Append(f->Block(), [&] { //
- b.Return(nullptr);
+ auto* r = b.Return(f, 42_i);
+ r->SetResults(Vector{b.InstructionResult(ty.i32())});
});
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(), R"(:3:5 error: return: undefined function
+ EXPECT_EQ(res.Failure().reason.Str(), R"(:3:5 error: return: expected exactly 0 results, got 1
+ ret 42i
+ ^^^^^^^
+
+:2:3 note: in block
+ $B1: {
+ ^^^
+
+note: # Disassembly
+%my_func = func():i32 {
+ $B1: {
+ ret 42i
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Return_NotFunction) {
+ auto* f = b.Function("my_func", ty.void_());
+ b.Append(f->Block(), [&] { //
+ auto* var = b.Var(ty.ptr<function, f32>());
+ auto* r = b.Return(nullptr);
+ r->SetOperand(0, var->Result(0));
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(), R"(:4:5 error: return: expected function for first operand
+ ret
+ ^^^
+
+:2:3 note: in block
+ $B1: {
+ ^^^
+
+note: # Disassembly
+%my_func = func():void {
+ $B1: {
+ %2:ptr<function, f32, read_write> = var
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Return_MissingFunction) {
+ auto* f = b.Function("my_func", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* r = b.Return(f);
+ r->ClearOperands();
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:3:5 error: return: expected between 1 and 2 operands, got 0
ret
^^^