tint/resolver: Materialize RHS of non-phony assignments
Phony assignments do not materialize the RHS. See: https://github.com/gpuweb/gpuweb/pull/2968 Bug: tint:1504 Change-Id: I29bb15e813d2b01876b5ec670c31b7aff3230986 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91963 Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
34f42aa453
commit
22bd004409
|
@ -147,29 +147,33 @@ TEST_F(ResolverAssignmentValidationTest, AssignToScalar_Fail) {
|
|||
// var my_var : i32 = 2i;
|
||||
// 1 = my_var;
|
||||
|
||||
auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
|
||||
WrapInFunction(var, Assign(Expr(Source{{12, 34}}, 1_i), "my_var"));
|
||||
WrapInFunction(Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2_i)), //
|
||||
Assign(Expr(Source{{12, 34}}, 1_i), "my_var"));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: cannot assign to value of type 'i32'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypes_Pass) {
|
||||
// var a : i32 = 2i;
|
||||
// a = 2i
|
||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
|
||||
WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2_i));
|
||||
// var a : i32 = 1i;
|
||||
// a = 2i;
|
||||
// a = 3;
|
||||
WrapInFunction(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(1_i)), //
|
||||
Assign("a", 2_i), //
|
||||
Assign("a", 3_a));
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesThroughAlias_Pass) {
|
||||
// alias myint = i32;
|
||||
// var a : myint = 2i;
|
||||
// a = 2
|
||||
auto* myint = Alias("myint", ty.i32());
|
||||
auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2_i));
|
||||
WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2_i));
|
||||
// alias myint = u32;
|
||||
// var a : myint = 1u;
|
||||
// a = 2u;
|
||||
// a = 3;
|
||||
auto* myint = Alias("myint", ty.u32());
|
||||
WrapInFunction(Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(1_u)), //
|
||||
Assign("a", 2_u), //
|
||||
Assign("a", 3_a));
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
@ -178,9 +182,9 @@ TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesInferRHSLoad_Pass)
|
|||
// var a : i32 = 2i;
|
||||
// var b : i32 = 3i;
|
||||
// a = b;
|
||||
auto* var_a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
|
||||
auto* var_b = Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3_i));
|
||||
WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, "a", "b"));
|
||||
WrapInFunction(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2_i)), //
|
||||
Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3_i)), //
|
||||
Assign("a", "b"));
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
@ -190,14 +194,26 @@ TEST_F(ResolverAssignmentValidationTest, AssignThroughPointer_Pass) {
|
|||
// let b : ptr<function,i32> = &a;
|
||||
// *b = 2i;
|
||||
const auto func = ast::StorageClass::kFunction;
|
||||
auto* var_a = Var("a", ty.i32(), func, Expr(2_i));
|
||||
auto* var_b = Let("b", ty.pointer<i32>(func), AddressOf(Expr("a")));
|
||||
WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, Deref("b"), 2_i));
|
||||
WrapInFunction(Var("a", ty.i32(), func, Expr(2_i)), //
|
||||
Let("b", ty.pointer<i32>(func), AddressOf(Expr("a"))), //
|
||||
Assign(Deref("b"), 2_i));
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignToConstant_Fail) {
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignMaterializedThroughPointer_Pass) {
|
||||
// var a : i32;
|
||||
// let b : ptr<function,i32> = &a;
|
||||
// *b = 2;
|
||||
const auto func = ast::StorageClass::kFunction;
|
||||
auto* var_a = Var("a", ty.i32(), func, Expr(2_i));
|
||||
auto* var_b = Let("b", ty.pointer<i32>(func), AddressOf(Expr("a")));
|
||||
WrapInFunction(var_a, var_b, Assign(Deref("b"), 2_a));
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignToLet_Fail) {
|
||||
// {
|
||||
// let a : i32 = 2i;
|
||||
// a = 2i
|
||||
|
@ -328,8 +344,12 @@ TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
|
|||
// fn f() {
|
||||
// _ = 1i;
|
||||
// _ = 2u;
|
||||
// _ = 3.0;
|
||||
// _ = vec2<bool>();
|
||||
// _ = 3.0f;
|
||||
// _ = 4;
|
||||
// _ = 5.0;
|
||||
// _ = vec2(6);
|
||||
// _ = vec3(7.0);
|
||||
// _ = vec4<bool>();
|
||||
// _ = tex;
|
||||
// _ = smp;
|
||||
// _ = &s;
|
||||
|
@ -354,7 +374,11 @@ TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
|
|||
WrapInFunction(Assign(Phony(), 1_i), //
|
||||
Assign(Phony(), 2_u), //
|
||||
Assign(Phony(), 3_f), //
|
||||
Assign(Phony(), vec2<bool>()), //
|
||||
Assign(Phony(), 4_a), //
|
||||
Assign(Phony(), 5.0_a), //
|
||||
Assign(Phony(), vec(nullptr, 2u, 6_a)), //
|
||||
Assign(Phony(), vec(nullptr, 3u, 7.0_a)), //
|
||||
Assign(Phony(), vec4<bool>()), //
|
||||
Assign(Phony(), "tex"), //
|
||||
Assign(Phony(), "smp"), //
|
||||
Assign(Phony(), AddressOf("s")), //
|
||||
|
|
|
@ -81,6 +81,13 @@ enum class Method {
|
|||
// let a : target_type = abstract_expr;
|
||||
kLet,
|
||||
|
||||
// var a : target_type;
|
||||
// a = abstract_expr;
|
||||
kAssign,
|
||||
|
||||
// _ = abstract_expr;
|
||||
kPhonyAssign,
|
||||
|
||||
// fn F(v : target_type) {}
|
||||
// fn x() {
|
||||
// F(abstract_expr);
|
||||
|
@ -147,6 +154,10 @@ static std::ostream& operator<<(std::ostream& o, Method m) {
|
|||
return o << "var";
|
||||
case Method::kLet:
|
||||
return o << "let";
|
||||
case Method::kAssign:
|
||||
return o << "assign";
|
||||
case Method::kPhonyAssign:
|
||||
return o << "phony-assign";
|
||||
case Method::kFnArg:
|
||||
return o << "fn-arg";
|
||||
case Method::kBuiltinArg:
|
||||
|
@ -251,6 +262,12 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
|
|||
case Method::kLet:
|
||||
WrapInFunction(Decl(Let("a", target_ty(), abstract_expr)));
|
||||
break;
|
||||
case Method::kAssign:
|
||||
WrapInFunction(Decl(Var("a", target_ty(), nullptr)), Assign("a", abstract_expr));
|
||||
break;
|
||||
case Method::kPhonyAssign:
|
||||
WrapInFunction(Assign(Phony(), abstract_expr));
|
||||
break;
|
||||
case Method::kFnArg:
|
||||
Func("F", {Param("P", target_ty())}, ty.void_(), {});
|
||||
WrapInFunction(CallStmt(Call("F", abstract_expr)));
|
||||
|
@ -364,20 +381,20 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
|
|||
|
||||
/// Methods that support scalar materialization
|
||||
constexpr Method kScalarMethods[] = {
|
||||
Method::kLet, Method::kVar, Method::kFnArg, Method::kBuiltinArg,
|
||||
Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg, Method::kBuiltinArg,
|
||||
Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
|
||||
};
|
||||
|
||||
/// Methods that support vector materialization
|
||||
constexpr Method kVectorMethods[] = {
|
||||
Method::kLet, Method::kVar, Method::kFnArg, Method::kBuiltinArg,
|
||||
Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg, Method::kBuiltinArg,
|
||||
Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
|
||||
};
|
||||
|
||||
/// Methods that support matrix materialization
|
||||
constexpr Method kMatrixMethods[] = {
|
||||
Method::kLet, Method::kVar, Method::kFnArg, Method::kReturn,
|
||||
Method::kArray, Method::kStruct, Method::kBinaryOp,
|
||||
Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg,
|
||||
Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
|
||||
};
|
||||
|
||||
/// Methods that support materialization for switch cases
|
||||
|
@ -388,6 +405,12 @@ constexpr Method kSwitchMethods[] = {
|
|||
Method::kSwitchCaseWithAbstractCase,
|
||||
};
|
||||
|
||||
/// Methods that do not materialize
|
||||
constexpr Method kNoMaterializeMethods[] = {
|
||||
Method::kPhonyAssign,
|
||||
// TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops:
|
||||
// Method::kBuiltinArg, Method::kBinaryOp,
|
||||
};
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
MaterializeScalar,
|
||||
MaterializeAbstractNumericToConcreteType,
|
||||
|
@ -486,18 +509,18 @@ INSTANTIATE_TEST_SUITE_P(MaterializeWorkgroupSize,
|
|||
Types<u32, AInt>(65535_a, 65535.0), //
|
||||
})));
|
||||
|
||||
// TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops.
|
||||
INSTANTIATE_TEST_SUITE_P(DISABLED_NoMaterialize,
|
||||
INSTANTIATE_TEST_SUITE_P(NoMaterialize,
|
||||
MaterializeAbstractNumericToConcreteType,
|
||||
testing::Combine(testing::Values(Expectation::kNoMaterialize),
|
||||
testing::Values(Method::kBuiltinArg, Method::kBinaryOp),
|
||||
testing::ValuesIn(kNoMaterializeMethods),
|
||||
testing::ValuesIn(std::vector<Data>{
|
||||
Types<AInt, AInt>(), //
|
||||
Types<AFloat, AFloat>(), //
|
||||
Types<AIntV, AIntV>(), //
|
||||
Types<AFloatV, AFloatV>(), //
|
||||
Types<AFloatM, AFloatM>(), //
|
||||
Types<AInt, AInt>(1_a, 1_a), //
|
||||
Types<AIntV, AIntV>(1_a, 1_a), //
|
||||
Types<AFloat, AFloat>(1.0_a, 1.0_a), //
|
||||
Types<AFloatV, AFloatV>(1.0_a, 1.0_a), //
|
||||
Types<AFloatM, AFloatM>(1.0_a, 1.0_a), //
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(InvalidConversion,
|
||||
MaterializeAbstractNumericToConcreteType,
|
||||
testing::Combine(testing::Values(Expectation::kInvalidConversion),
|
||||
|
|
|
@ -2409,14 +2409,23 @@ sem::Statement* Resolver::AssignmentStatement(const ast::AssignmentStatement* st
|
|||
return false;
|
||||
}
|
||||
|
||||
auto* rhs = Expression(stmt->rhs);
|
||||
const bool is_phony_assignment = stmt->lhs->Is<ast::PhonyExpression>();
|
||||
|
||||
const auto* rhs = Expression(stmt->rhs);
|
||||
if (!rhs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_phony_assignment) {
|
||||
rhs = Materialize(rhs, lhs->Type()->UnwrapRef());
|
||||
if (!rhs) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto& behaviors = sem->Behaviors();
|
||||
behaviors = rhs->Behaviors();
|
||||
if (!stmt->lhs->Is<ast::PhonyExpression>()) {
|
||||
if (!is_phony_assignment) {
|
||||
behaviors.Add(lhs->Behaviors());
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "src/tint/ast/variable_decl_statement.h"
|
||||
#include "src/tint/ast/vector.h"
|
||||
#include "src/tint/ast/workgroup_attribute.h"
|
||||
#include "src/tint/sem/abstract_numeric.h"
|
||||
#include "src/tint/sem/array.h"
|
||||
#include "src/tint/sem/atomic.h"
|
||||
#include "src/tint/sem/call.h"
|
||||
|
@ -2141,7 +2142,8 @@ bool Validator::Assignment(const ast::Statement* a, const sem::Type* rhs_ty) con
|
|||
if (lhs->Is<ast::PhonyExpression>()) {
|
||||
// https://www.w3.org/TR/WGSL/#phony-assignment-section
|
||||
auto* ty = rhs_ty->UnwrapRef();
|
||||
if (!ty->IsConstructible() && !ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler>()) {
|
||||
if (!ty->IsConstructible() &&
|
||||
!ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler, sem::AbstractNumeric>()) {
|
||||
AddError("cannot assign '" + sem_.TypeNameOf(rhs_ty) +
|
||||
"' to '_'. '_' can only be assigned a constructible, pointer, "
|
||||
"texture or sampler type",
|
||||
|
|
Loading…
Reference in New Issue