Implement phony assignment

Bug: tint:1213
Change-Id: Ib1ebc4947405c4ada7a9bdbc6bd5a36447bbd234
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/67064
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton
2021-10-21 23:04:44 +00:00
committed by Tint LUCI CQ
parent 092326894e
commit 1aa98e62c0
47 changed files with 1081 additions and 25 deletions

View File

@@ -293,6 +293,111 @@ TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_RuntimeArray) {
"56:78 error: storage type of assignment must be constructible");
}
TEST_F(ResolverAssignmentValidationTest,
AssignToPhony_NonConstructableStruct_Fail) {
// [[block]]
// struct S {
// arr: array<i32>;
// };
// [[group(0), binding(0)]] var<storage, read_write> s : S;
// fn f() {
// _ = s;
// }
auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot assign 'S' to '_'. "
"'_' can only be assigned a constructable, pointer, texture or "
"sampler type");
}
TEST_F(ResolverAssignmentValidationTest, AssignToPhony_DynamicArray_Fail) {
// [[block]]
// struct S {
// arr: array<i32>;
// };
// [[group(0), binding(0)]] var<storage, read_write> s : S;
// fn f() {
// _ = s.arr;
// }
auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
"12:34 error: cannot assign 'ref<storage, array<i32>, read>' to '_'. "
"'_' can only be assigned a constructable, pointer, texture or sampler "
"type");
}
TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
// [[block]]
// struct S {
// i: i32;
// arr: array<i32>;
// };
// [[block]]
// struct U {
// i: i32;
// };
// [[group(0), binding(0)]] var tex texture_2d;
// [[group(0), binding(1)]] var smp sampler;
// [[group(0), binding(2)]] var<uniform> u : U;
// [[group(0), binding(3)]] var<storage, read_write> s : S;
// var<workgroup> wg : array<f32, 10>
// fn f() {
// _ = 1;
// _ = 2u;
// _ = 3.0;
// _ = vec2<bool>();
// _ = tex;
// _ = smp;
// _ = &s;
// _ = s.i;
// _ = &s.arr;
// _ = u;
// _ = u.i;
// _ = wg;
// _ = wg[3];
// }
auto* S = Structure("S",
{
Member("i", ty.i32()),
Member("arr", ty.array<i32>()),
},
{StructBlock()});
auto* U = Structure("U", {Member("i", ty.i32())}, {StructBlock()});
Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(0, 0));
Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
Global("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
Global("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
Global("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
WrapInFunction(Assign(Phony(), 1), //
Assign(Phony(), 2), //
Assign(Phony(), 3), //
Assign(Phony(), vec2<bool>()), //
Assign(Phony(), "tex"), //
Assign(Phony(), "smp"), //
Assign(Phony(), AddressOf("s")), //
Assign(Phony(), MemberAccessor("s", "i")), //
Assign(Phony(), AddressOf(MemberAccessor("s", "arr"))), //
Assign(Phony(), "u"), //
Assign(Phony(), MemberAccessor("u", "i")), //
Assign(Phony(), "wg"), //
Assign(Phony(), IndexAccessor("wg", 3)));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
} // namespace resolver
} // namespace tint

View File

@@ -2303,6 +2303,8 @@ bool Resolver::Expression(const ast::Expression* root) {
ok = MemberAccessor(member);
} else if (auto* unary = expr->As<ast::UnaryOpExpression>()) {
ok = UnaryOp(unary);
} else if (expr->Is<ast::PhonyExpression>()) {
ok = true; // No-op
} else {
TINT_ICE(Resolver, diagnostics_)
<< "unhandled expression type: " << expr->TypeInfo().name;
@@ -4393,13 +4395,30 @@ bool Resolver::Assignment(const ast::AssignmentStatement* a) {
if (!Expression(a->lhs) || !Expression(a->rhs)) {
return false;
}
return ValidateAssignment(a);
}
bool Resolver::ValidateAssignment(const ast::AssignmentStatement* a) {
auto const* rhs_type = TypeOf(a->rhs);
if (a->lhs->Is<ast::PhonyExpression>()) {
// https://www.w3.org/TR/WGSL/#phony-assignment-section
auto* ty = rhs_type->UnwrapRef();
if (!ty->IsConstructible() &&
!ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler>()) {
AddError(
"cannot assign '" + TypeNameOf(a->rhs) +
"' to '_'. '_' can only be assigned a constructable, pointer, "
"texture or sampler type",
a->rhs->source);
return false;
}
return true; // RHS can be anything.
}
// https://gpuweb.github.io/gpuweb/wgsl/#assignment-statement
auto const* lhs_type = TypeOf(a->lhs);
auto const* rhs_type = TypeOf(a->rhs);
if (auto* ident = a->lhs->As<ast::IdentifierExpression>()) {
VariableInfo* var;