validation: limit dynamic indexes to references to matrices and arrays

https://github.com/gpuweb/gpuweb/pull/1801
indexes must be of type 'i32' or 'u32'

Bug: tint:867
Change-Id: Ie5bfacf87af5a06d5428dc510145e96fb156c42e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54720
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Sarah Mashayekhi <sarahmashay@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
Sarah 2021-06-16 18:50:13 +00:00 committed by Sarah Mashayekhi
parent b6fdcc54df
commit 10442eff7d
5 changed files with 187 additions and 57 deletions

View File

@ -1640,8 +1640,9 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
if (!Expression(expr->array())) {
return false;
}
Mark(expr->idx_expr());
if (!Expression(expr->idx_expr())) {
auto* idx = expr->idx_expr();
Mark(idx);
if (!Expression(idx)) {
return false;
}
@ -1661,6 +1662,26 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
return false;
}
if (!TypeOf(idx)->UnwrapRef()->IsAnyOf<sem::I32, sem::U32>()) {
diagnostics_.add_error("index must be of type 'i32' or 'u32', found: '" +
TypeNameOf(idx) + "'",
idx->source());
return false;
}
if (parent_type->Is<sem::Array>() || parent_type->Is<sem::Matrix>()) {
if (!res->Is<sem::Reference>()) {
// TODO(bclayton): expand this to allow any const_expr expression
// https://github.com/gpuweb/gpuweb/issues/1272
auto* scalar = idx->As<ast::ScalarConstructorExpression>();
if (!scalar || !scalar->literal()->As<ast::IntLiteral>()) {
diagnostics_.add_error(
"index must be signed or unsigned integer literal", idx->source());
return false;
}
}
}
// If we're extracting from a reference, we return a reference.
if (auto* ref = res->As<sem::Reference>()) {
ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),

View File

@ -476,6 +476,69 @@ TEST_F(ResolverTest, Expr_ArrayAccessor_Array_Constant) {
EXPECT_TRUE(TypeOf(acc)->Is<sem::F32>()) << TypeOf(acc)->type_name();
}
TEST_F(ResolverTest, ArrayAccessor_Matrix_Dynamic_F32) {
Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kInput);
auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 1.0f));
WrapInFunction(acc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
}
TEST_F(ResolverTest, ArrayAccessor_Matrix_Dynamic_Ref) {
Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kInput);
auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
auto* acc = IndexAccessor("my_var", idx);
WrapInFunction(Decl(idx), acc);
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, ArrayAccessor_Matrix_BothDimensions_Dynamic_Ref) {
Global("my_var", ty.mat4x4<bool>(), ast::StorageClass::kOutput);
auto* idx = Var("idx", ty.u32(), Expr(3u));
auto* idy = Var("idy", ty.u32(), Expr(2u));
auto* acc = IndexAccessor(IndexAccessor("my_var", idx), idy);
WrapInFunction(Decl(idx), Decl(idy), acc);
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, ArrayAccessor_Matrix_Dynamic) {
GlobalConst("my_const", ty.mat2x3<f32>(), Construct(ty.mat2x3<f32>()));
auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
auto* acc = IndexAccessor("my_const", Expr(Source{{12, 34}}, idx));
WrapInFunction(Decl(idx), acc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be signed or unsigned integer literal");
}
TEST_F(ResolverTest, ArrayAccessor_Matrix_XDimension_Dynamic) {
GlobalConst("my_var", ty.mat4x4<bool>(), Construct(ty.mat4x4<bool>()));
auto* idx = Var("idx", ty.u32(), Expr(3u));
auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
WrapInFunction(Decl(idx), acc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be signed or unsigned integer literal");
}
TEST_F(ResolverTest, ArrayAccessor_Matrix_BothDimension_Dynamic) {
GlobalConst("my_var", ty.mat4x4<bool>(), Construct(ty.mat4x4<bool>()));
auto* idx = Var("idy", ty.u32(), Expr(2u));
auto* acc =
IndexAccessor(IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)), 1);
WrapInFunction(Decl(idx), acc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be signed or unsigned integer literal");
}
TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix) {
Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kInput);
@ -507,6 +570,34 @@ TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix_BothDimensions) {
EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_ArrayAccessor_Vector_F32) {
Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 2.0f));
WrapInFunction(acc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
}
TEST_F(ResolverTest, Expr_ArrayAccessor_Vector_Dynamic_Ref) {
Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
auto* idx = Var("idx", ty.i32(), Expr(2));
auto* acc = IndexAccessor("my_var", idx);
WrapInFunction(Decl(idx), acc);
EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverTest, Expr_ArrayAccessor_Vector_Dynamic) {
GlobalConst("my_var", ty.vec3<f32>(), Construct(ty.vec3<f32>()));
auto* idx = Var("idx", ty.i32(), Expr(2));
auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
WrapInFunction(Decl(idx), acc);
EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverTest, Expr_ArrayAccessor_Vector) {
Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
@ -698,6 +789,76 @@ TEST_F(ResolverTest, Expr_Identifier_FunctionVariable_Const) {
EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
}
TEST_F(ResolverTest, ArrayAccessor_Dynamic_Ref_F32) {
// var a : array<bool, 10> = 0;
// var idx : f32 = f32();
// var f : f32 = a[idx];
auto* a = Var("a", ty.array<bool, 10>(), array<bool, 10>());
auto* idx = Var("idx", ty.f32(), Construct(ty.f32()));
auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
Func("my_func", ast::VariableList{}, ty.void_(),
{
Decl(a),
Decl(idx),
Decl(f),
},
ast::DecorationList{});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
}
TEST_F(ResolverTest, ArrayAccessor_Dynamic_I32) {
// let a : array<f32, 3> = 0;
// var idx : i32 = 0;
// var f : f32 = a[idx];
auto* a = Const("a", ty.array<f32, 3>(), array<f32, 3>());
auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
Func("my_func", ast::VariableList{}, ty.void_(),
{
Decl(a),
Decl(idx),
Decl(f),
},
ast::DecorationList{});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be signed or unsigned integer literal");
}
TEST_F(ResolverTest, ArrayAccessor_Literal_F32) {
// let a : array<f32, 3>;
// var f : f32 = a[2.0f];
auto* a = Const("a", ty.array<f32, 3>(), array<f32, 3>());
auto* f =
Var("a_2", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, 2.0f)));
Func("my_func", ast::VariableList{}, ty.void_(),
{
Decl(a),
Decl(f),
},
ast::DecorationList{});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
}
TEST_F(ResolverTest, ArrayAccessor_Literal_I32) {
// let a : array<f32, 3>;
// var f : f32 = a[2];
auto* a = Const("a", ty.array<f32, 3>(), array<f32, 3>());
auto* f = Var("a_2", ty.f32(), IndexAccessor("a", 2));
Func("my_func", ast::VariableList{}, ty.void_(),
{
Decl(a),
Decl(f),
},
ast::DecorationList{});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, Expr_Identifier_FunctionVariable) {
auto* my_var_a = Expr("my_var");
auto* my_var_b = Expr("my_var");

View File

@ -2063,7 +2063,7 @@ TEST_P(MatrixConstructorTest,
const auto param = GetParam();
// Skip the test if parameters would have resuled in an invalid vec1 type.
// Skip the test if parameters would have resulted in an invalid vec1 type.
if (param.rows == 2) {
return;
}

View File

@ -52,7 +52,7 @@ TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) {
auto* src = R"(
var<private> a : array<f32, 3>;
var<private> b : array<f32, 5>;
var<private> b : array<i32, 5>;
var<private> i : u32;
@ -64,7 +64,7 @@ fn f() {
auto* expect = R"(
var<private> a : array<f32, 3>;
var<private> b : array<f32, 5>;
var<private> b : array<i32, 5>;
var<private> i : u32;

View File

@ -225,58 +225,6 @@ let module_str : S = S(1, 2.0, 3);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, Bug406Array) {
// See crbug.com/tint/406
auto* src = R"(
[[block]]
struct Uniforms {
transform : mat2x2<f32>;
};
[[group(0), binding(0)]] var<uniform> ubo : Uniforms;
[[builtin(vertex_index)]] var<in> vertex_index : u32;
[[builtin(position)]] var<out> position : vec4<f32>;
[[stage(vertex)]]
fn main() {
let transform : mat2x2<f32> = ubo.transform;
var coord : vec2<f32> = array<vec2<f32>, 3>(
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, 1.0),
vec2<f32>(-1.0, -1.0)
)[vertex_index];
position = vec4<f32>(transform * coord, 0.0, 1.0);
}
)";
auto* expect = R"(
[[block]]
struct Uniforms {
transform : mat2x2<f32>;
};
[[group(0), binding(0)]] var<uniform> ubo : Uniforms;
[[builtin(vertex_index)]] var<in> vertex_index : u32;
[[builtin(position)]] var<out> position : vec4<f32>;
[[stage(vertex)]]
fn main() {
let transform : mat2x2<f32> = ubo.transform;
let tint_symbol : array<vec2<f32>, 3> = array<vec2<f32>, 3>(vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, -1.0));
var coord : vec2<f32> = tint_symbol[vertex_index];
position = vec4<f32>((transform * coord), 0.0, 1.0);
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, EmptyModule) {
auto* src = "";
auto* expect = "";