[tint][ir][val] Improve checks for variable initializers
- Makes sure that initializers are only used for private or function
address space variables
- Catches an annoying case where an ir::Function has been supplied as
a initializer
Fixes: 381219432
Change-Id: I12b9fc9aae7323d7d8726acbe5800e02d25eb9dd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/217014
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
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 70fc255..098aaee 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -2350,21 +2350,6 @@
return;
}
- // Check that initializer and result type match
- if (var->Initializer()) {
- if (!CheckOperand(var, ir::Var::kInitializerOperandOffset)) {
- return;
- }
-
- if (var->Initializer()->Type() != var->Result(0)->Type()->UnwrapPtrOrRef()) {
- AddError(var) << "initializer type "
- << style::Type(var->Initializer()->Type()->FriendlyName())
- << " does not match store type "
- << style::Type(var->Result(0)->Type()->UnwrapPtrOrRef()->FriendlyName());
- return;
- }
- }
-
auto* result_type = var->Result(0)->Type();
if (result_type == nullptr) {
AddError(var) << "result type is undefined";
@@ -2385,6 +2370,42 @@
}
}
+ // Check that initializer and result type match
+ if (var->Initializer()) {
+ if (mv->AddressSpace() != AddressSpace::kFunction &&
+ mv->AddressSpace() != AddressSpace::kPrivate) {
+ AddError(var)
+ << "only variables in the function or private address space may be initialized";
+ return;
+ }
+
+ if (!CheckOperand(var, ir::Var::kInitializerOperandOffset)) {
+ return;
+ }
+
+ if (var->Operand(ir::Var::kInitializerOperandOffset)->Is<ir::Function>()) {
+ // ir::Function has a null ->Type(), and won't be filtered by CheckOperand, so needs
+ // special handling here
+ AddError(var) << "initializer type 'function' does not match store type "
+ << style::Type(var->Result(0)->Type()->UnwrapPtrOrRef()->FriendlyName());
+ return;
+ }
+
+ if (var->Initializer()->Type() != var->Result(0)->Type()->UnwrapPtrOrRef()) {
+ AddError(var) << "initializer type "
+ << style::Type(var->Initializer()->Type()->FriendlyName())
+ << " does not match store type "
+ << style::Type(var->Result(0)->Type()->UnwrapPtrOrRef()->FriendlyName());
+ return;
+ }
+ }
+
+ if (auto result = ValidateBindingPoint(var->BindingPoint(), mv->AddressSpace());
+ result != Success) {
+ AddError(var) << result.Failure();
+ return;
+ }
+
if (var->Block() == mod_.root_block && mv->AddressSpace() == AddressSpace::kFunction) {
AddError(var) << "vars in the 'function' address space must be in a function scope";
return;
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index a9dfb11..b5be47b 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -5411,6 +5411,85 @@
)");
}
+TEST_F(IR_ValidatorTest, Var_Init_FunctionTypeInit) {
+ auto* invalid = b.Function("invalid_init", ty.void_());
+ b.Append(invalid->Block(), [&] { b.Return(invalid); });
+ auto* f = b.Function("my_func", ty.void_());
+
+ b.Append(f->Block(), [&] {
+ auto* i = b.Var<function, f32>("i");
+ i->SetInitializer(invalid);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:8:41 error: var: initializer type 'function' does not match store type 'f32'
+ %i:ptr<function, f32, read_write> = var, %invalid_init
+ ^^^
+
+:7:3 note: in block
+ $B2: {
+ ^^^
+
+note: # Disassembly
+%invalid_init = func():void {
+ $B1: {
+ ret
+ }
+}
+%my_func = func():void {
+ $B2: {
+ %i:ptr<function, f32, read_write> = var, %invalid_init
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Var_Init_InvalidAddressSpace) {
+ auto* p = b.Var<private_, f32>("p");
+ p->SetInitializer(b.Constant(1_f));
+ mod.root_block->Append(p);
+ auto* s = b.Var<storage, f32>("s");
+ s->SetInitializer(b.Constant(1_f));
+ mod.root_block->Append(s);
+ auto* f = b.Function("my_func", ty.void_());
+
+ b.Append(f->Block(), [&] {
+ auto* v = b.Var<function, f32>("v");
+ v->SetInitializer(b.Constant(1_f));
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:3:38 error: var: only variables in the function or private address space may be initialized
+ %s:ptr<storage, f32, read_write> = var, 1.0f
+ ^^^
+
+:1:1 note: in block
+$B1: { # root
+^^^
+
+note: # Disassembly
+$B1: { # root
+ %p:ptr<private, f32, read_write> = var, 1.0f
+ %s:ptr<storage, f32, read_write> = var, 1.0f
+}
+
+%my_func = func():void {
+ $B2: {
+ %v:ptr<function, f32, read_write> = var, 1.0f
+ ret
+ }
+}
+)");
+}
+
TEST_F(IR_ValidatorTest, Var_HandleMissingBindingPoint) {
auto* v = b.Var(ty.ptr<handle, i32>());
mod.root_block->Append(v);