resolver: Add more inferred access tests

The tests that were in ptr_ref_* should have been in var_let_*. Move these to the right place, and add more tests that actually test the pointer access.

Bug: tint:846
Change-Id: I383fcbf7eeb4a1428cf50c52bc2958720458adcb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/53388
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2021-06-07 16:51:02 +00:00 committed by Tint LUCI CQ
parent fb6a6185fa
commit 224ce729cc
5 changed files with 186 additions and 91 deletions

View File

@ -58,7 +58,7 @@ TEST_F(ResolverPtrRefTest, AddressOfThenDeref) {
EXPECT_TRUE(TypeOf(expr)->As<sem::Reference>()->StoreType()->Is<sem::I32>()); EXPECT_TRUE(TypeOf(expr)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
} }
TEST_F(ResolverPtrRefTest, DefaultStorageClass) { TEST_F(ResolverPtrRefTest, DefaultPtrStorageClass) {
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
auto* buf = Structure("S", {Member("m", ty.i32())}, auto* buf = Structure("S", {Member("m", ty.i32())},
@ -76,91 +76,44 @@ TEST_F(ResolverPtrRefTest, DefaultStorageClass) {
create<ast::BindingDecoration>(1), create<ast::BindingDecoration>(1),
create<ast::GroupDecoration>(0), create<ast::GroupDecoration>(0),
}); });
auto* handle = Global("h", ty.depth_texture(ast::TextureDimension::k2d),
ast::DecorationList{
create<ast::BindingDecoration>(2),
create<ast::GroupDecoration>(0),
});
WrapInFunction(function); auto* function_ptr =
Const("f_ptr", ty.pointer(ty.i32(), ast::StorageClass::kFunction),
AddressOf(function));
auto* private_ptr =
Const("p_ptr", ty.pointer(ty.i32(), ast::StorageClass::kPrivate),
AddressOf(private_));
auto* workgroup_ptr =
Const("w_ptr", ty.pointer(ty.i32(), ast::StorageClass::kWorkgroup),
AddressOf(workgroup));
auto* uniform_ptr =
Const("ub_ptr", ty.pointer(buf, ast::StorageClass::kUniform),
AddressOf(uniform));
auto* storage_ptr =
Const("sb_ptr", ty.pointer(buf, ast::StorageClass::kStorage),
AddressOf(storage));
WrapInFunction(function, function_ptr, private_ptr, workgroup_ptr,
uniform_ptr, storage_ptr);
EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(function)->Is<sem::Reference>()); ASSERT_TRUE(TypeOf(function_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>()); ASSERT_TRUE(TypeOf(private_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(workgroup)->Is<sem::Reference>()); ASSERT_TRUE(TypeOf(workgroup_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(uniform)->Is<sem::Reference>()); ASSERT_TRUE(TypeOf(uniform_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>()); ASSERT_TRUE(TypeOf(storage_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(handle)->Is<sem::Reference>());
EXPECT_EQ(TypeOf(function)->As<sem::Reference>()->Access(), EXPECT_EQ(TypeOf(function_ptr)->As<sem::Pointer>()->Access(),
ast::Access::kReadWrite); ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(private_)->As<sem::Reference>()->Access(), EXPECT_EQ(TypeOf(private_ptr)->As<sem::Pointer>()->Access(),
ast::Access::kReadWrite); ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(workgroup)->As<sem::Reference>()->Access(), EXPECT_EQ(TypeOf(workgroup_ptr)->As<sem::Pointer>()->Access(),
ast::Access::kReadWrite); ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(uniform)->As<sem::Reference>()->Access(), EXPECT_EQ(TypeOf(uniform_ptr)->As<sem::Pointer>()->Access(),
ast::Access::kRead); ast::Access::kRead);
EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), EXPECT_EQ(TypeOf(storage_ptr)->As<sem::Pointer>()->Access(),
ast::Access::kRead); ast::Access::kRead);
EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
}
TEST_F(ResolverPtrRefTest, ExplicitStorageClass) {
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class
auto* buf = Structure("S", {Member("m", ty.i32())},
{create<ast::StructBlockDecoration>()});
auto* storage =
Global("sb", buf, ast::StorageClass::kStorage, ast::Access::kReadWrite,
ast::DecorationList{
create<ast::BindingDecoration>(1),
create<ast::GroupDecoration>(0),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
}
TEST_F(ResolverPtrRefTest, InheritsAccessFromOriginatingVariable) {
// struct Inner {
// arr: array<i32, 4>;
// }
// [[block]] struct S {
// inner: Inner;
// }
// [[group(0), binding(0)]] var<storage, read_write> s : S;
// fn f() {
// let p = &s.inner.arr[2];
// }
auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
auto* buf = Structure("S", {Member("inner", inner)},
{create<ast::StructBlockDecoration>()});
auto* storage =
Global("s", buf, ast::StorageClass::kStorage, ast::Access::kReadWrite,
ast::DecorationList{
create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0),
});
auto* expr =
IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
auto* ptr = Const("p", nullptr, AddressOf(expr));
WrapInFunction(ptr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
EXPECT_EQ(TypeOf(expr)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
} }
} // namespace } // namespace

View File

@ -78,21 +78,22 @@ TEST_F(ResolverPtrRefValidationTest, DerefOfVar) {
"12:34 error: cannot dereference expression of type 'i32'"); "12:34 error: cannot dereference expression of type 'i32'");
} }
TEST_F(ResolverPtrRefValidationTest, InferredAccessMismatch) { TEST_F(ResolverPtrRefValidationTest, InferredPtrAccessMismatch) {
// struct Inner { // struct Inner {
// arr: array<i32, 4>; // arr: array<i32, 4>;
// } // }
// [[block]] struct S { // [[block]] struct S {
// inner: Inner; // inner: Inner;
// } // }
// [[group(0), binding(0)]] var<storage> s : S; // [[group(0), binding(0)]] var<storage, read_write> s : S;
// fn f() { // fn f() {
// let p : pointer<storage, i32, read_write> = &s.inner.arr[2]; // let p : pointer<storage, i32> = &s.inner.arr[2];
// } // }
auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())}); auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
auto* buf = Structure("S", {Member("inner", inner)}, auto* buf = Structure("S", {Member("inner", inner)},
{create<ast::StructBlockDecoration>()}); {create<ast::StructBlockDecoration>()});
auto* storage = Global("s", buf, ast::StorageClass::kStorage, auto* storage =
Global("s", buf, ast::StorageClass::kStorage, ast::Access::kReadWrite,
ast::DecorationList{ ast::DecorationList{
create<ast::BindingDecoration>(0), create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0), create<ast::GroupDecoration>(0),
@ -100,9 +101,8 @@ TEST_F(ResolverPtrRefValidationTest, InferredAccessMismatch) {
auto* expr = auto* expr =
IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4); IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
auto* ptr = Const( auto* ptr =
Source{{12, 34}}, "p", Const(Source{{12, 34}}, "p", ty.pointer<i32>(ast::StorageClass::kStorage),
ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite),
AddressOf(expr)); AddressOf(expr));
WrapInFunction(ptr); WrapInFunction(ptr);
@ -110,8 +110,8 @@ TEST_F(ResolverPtrRefValidationTest, InferredAccessMismatch) {
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), EXPECT_EQ(r()->error(),
"12:34 error: cannot initialize let of type " "12:34 error: cannot initialize let of type "
"'ptr<storage, i32, read_write>' with value of type " "'ptr<storage, i32>' with value of type "
"'ptr<storage, i32, read>'"); "'ptr<storage, i32, read_write>'");
} }
} // namespace } // namespace

View File

@ -511,7 +511,6 @@ ast::AccessControl Resolver::DefaultAccessForStorageClass(
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
switch (storage_class) { switch (storage_class) {
case ast::StorageClass::kStorage: case ast::StorageClass::kStorage:
return ast::Access::kRead;
case ast::StorageClass::kUniform: case ast::StorageClass::kUniform:
case ast::StorageClass::kUniformConstant: case ast::StorageClass::kUniformConstant:
return ast::Access::kRead; return ast::Access::kRead;

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/ast/struct_block_decoration.h"
#include "src/resolver/resolver.h" #include "src/resolver/resolver.h"
#include "src/resolver/resolver_test_helper.h" #include "src/resolver/resolver_test_helper.h"
#include "src/sem/reference_type.h" #include "src/sem/reference_type.h"
@ -128,6 +129,111 @@ TEST_F(ResolverVarLetTest, TypeOfLet) {
EXPECT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>()); EXPECT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
} }
TEST_F(ResolverVarLetTest, DefaultVarStorageClass) {
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class
auto* buf = Structure("S", {Member("m", ty.i32())},
{create<ast::StructBlockDecoration>()});
auto* function = Var("f", ty.i32());
auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
auto* uniform = Global("ub", buf, ast::StorageClass::kUniform,
ast::DecorationList{
create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0),
});
auto* storage = Global("sb", buf, ast::StorageClass::kStorage,
ast::DecorationList{
create<ast::BindingDecoration>(1),
create<ast::GroupDecoration>(0),
});
auto* handle = Global("h", ty.depth_texture(ast::TextureDimension::k2d),
ast::DecorationList{
create<ast::BindingDecoration>(2),
create<ast::GroupDecoration>(0),
});
WrapInFunction(function);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(function)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(workgroup)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(uniform)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(handle)->Is<sem::Reference>());
EXPECT_EQ(TypeOf(function)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(private_)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(workgroup)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(uniform)->As<sem::Reference>()->Access(),
ast::Access::kRead);
EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(),
ast::Access::kRead);
EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
}
TEST_F(ResolverVarLetTest, ExplicitVarStorageClass) {
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class
auto* buf = Structure("S", {Member("m", ty.i32())},
{create<ast::StructBlockDecoration>()});
auto* storage =
Global("sb", buf, ast::StorageClass::kStorage, ast::Access::kReadWrite,
ast::DecorationList{
create<ast::BindingDecoration>(1),
create<ast::GroupDecoration>(0),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
}
TEST_F(ResolverVarLetTest, LetInheritsAccessFromOriginatingVariable) {
// struct Inner {
// arr: array<i32, 4>;
// }
// [[block]] struct S {
// inner: Inner;
// }
// [[group(0), binding(0)]] var<storage, read_write> s : S;
// fn f() {
// let p = &s.inner.arr[2];
// }
auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
auto* buf = Structure("S", {Member("inner", inner)},
{create<ast::StructBlockDecoration>()});
auto* storage =
Global("s", buf, ast::StorageClass::kStorage, ast::Access::kReadWrite,
ast::DecorationList{
create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0),
});
auto* expr =
IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
auto* ptr = Const("p", nullptr, AddressOf(expr));
WrapInFunction(ptr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
EXPECT_EQ(TypeOf(expr)->As<sem::Reference>()->Access(),
ast::Access::kReadWrite);
EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
}
} // namespace } // namespace
} // namespace resolver } // namespace resolver
} // namespace tint } // namespace tint

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/ast/struct_block_decoration.h"
#include "src/resolver/resolver.h" #include "src/resolver/resolver.h"
#include "src/resolver/resolver_test_helper.h" #include "src/resolver/resolver_test_helper.h"
@ -216,6 +217,42 @@ TEST_F(ResolverVarLetValidationTest, VarRedeclaredInIfBlock) {
EXPECT_EQ(r()->error(), "12:34 error v-0014: redeclared identifier 'v'"); EXPECT_EQ(r()->error(), "12:34 error v-0014: redeclared identifier 'v'");
} }
TEST_F(ResolverVarLetValidationTest, InferredPtrStorageAccessMismatch) {
// struct Inner {
// arr: array<i32, 4>;
// }
// [[block]] struct S {
// inner: Inner;
// }
// [[group(0), binding(0)]] var<storage> s : S;
// fn f() {
// let p : pointer<storage, i32, read_write> = &s.inner.arr[2];
// }
auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
auto* buf = Structure("S", {Member("inner", inner)},
{create<ast::StructBlockDecoration>()});
auto* storage = Global("s", buf, ast::StorageClass::kStorage,
ast::DecorationList{
create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0),
});
auto* expr =
IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
auto* ptr = Const(
Source{{12, 34}}, "p",
ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite),
AddressOf(expr));
WrapInFunction(ptr);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot initialize let of type "
"'ptr<storage, i32, read_write>' with value of type "
"'ptr<storage, i32, read>'");
}
} // namespace } // namespace
} // namespace resolver } // namespace resolver
} // namespace tint } // namespace tint