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:
parent
b6fdcc54df
commit
10442eff7d
|
@ -1640,8 +1640,9 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
|
||||||
if (!Expression(expr->array())) {
|
if (!Expression(expr->array())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Mark(expr->idx_expr());
|
auto* idx = expr->idx_expr();
|
||||||
if (!Expression(expr->idx_expr())) {
|
Mark(idx);
|
||||||
|
if (!Expression(idx)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1661,6 +1662,26 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
|
||||||
return false;
|
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 we're extracting from a reference, we return a reference.
|
||||||
if (auto* ref = res->As<sem::Reference>()) {
|
if (auto* ref = res->As<sem::Reference>()) {
|
||||||
ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
|
ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
|
||||||
|
|
|
@ -476,6 +476,69 @@ TEST_F(ResolverTest, Expr_ArrayAccessor_Array_Constant) {
|
||||||
EXPECT_TRUE(TypeOf(acc)->Is<sem::F32>()) << TypeOf(acc)->type_name();
|
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) {
|
TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix) {
|
||||||
Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kInput);
|
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>());
|
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) {
|
TEST_F(ResolverTest, Expr_ArrayAccessor_Vector) {
|
||||||
Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
|
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);
|
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) {
|
TEST_F(ResolverTest, Expr_Identifier_FunctionVariable) {
|
||||||
auto* my_var_a = Expr("my_var");
|
auto* my_var_a = Expr("my_var");
|
||||||
auto* my_var_b = Expr("my_var");
|
auto* my_var_b = Expr("my_var");
|
||||||
|
|
|
@ -2063,7 +2063,7 @@ TEST_P(MatrixConstructorTest,
|
||||||
|
|
||||||
const auto param = GetParam();
|
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) {
|
if (param.rows == 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) {
|
||||||
auto* src = R"(
|
auto* src = R"(
|
||||||
var<private> a : array<f32, 3>;
|
var<private> a : array<f32, 3>;
|
||||||
|
|
||||||
var<private> b : array<f32, 5>;
|
var<private> b : array<i32, 5>;
|
||||||
|
|
||||||
var<private> i : u32;
|
var<private> i : u32;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ fn f() {
|
||||||
auto* expect = R"(
|
auto* expect = R"(
|
||||||
var<private> a : array<f32, 3>;
|
var<private> a : array<f32, 3>;
|
||||||
|
|
||||||
var<private> b : array<f32, 5>;
|
var<private> b : array<i32, 5>;
|
||||||
|
|
||||||
var<private> i : u32;
|
var<private> i : u32;
|
||||||
|
|
||||||
|
|
|
@ -225,58 +225,6 @@ let module_str : S = S(1, 2.0, 3);
|
||||||
EXPECT_EQ(expect, str(got));
|
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) {
|
TEST_F(PromoteInitializersToConstVarTest, EmptyModule) {
|
||||||
auto* src = "";
|
auto* src = "";
|
||||||
auto* expect = "";
|
auto* expect = "";
|
||||||
|
|
Loading…
Reference in New Issue