tint/resolver: Track evaluation stage
Keep track of the earliest evaluation point for an expression.
Required to properly track what can be assigned to a `const`, `override`, `let`, `var`.
Bug: tint:1601
Bug: chromium:1343242
Change-Id: I301eec21b71e9036dc1bf6c9af8079317d724762
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95949
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 8d09d19..7fc09cc 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -367,10 +367,11 @@
sem::Variable* sem = nullptr;
if (is_global) {
sem = builder_->create<sem::GlobalVariable>(
- v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr,
- sem::BindingPoint{});
+ v, ty, sem::EvaluationStage::kRuntime, ast::StorageClass::kNone,
+ ast::Access::kUndefined, /* constant_value */ nullptr, sem::BindingPoint{});
} else {
- sem = builder_->create<sem::LocalVariable>(v, ty, ast::StorageClass::kNone,
+ sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime,
+ ast::StorageClass::kNone,
ast::Access::kUndefined, current_statement_,
/* constant_value */ nullptr);
}
@@ -421,8 +422,8 @@
}
auto* sem = builder_->create<sem::GlobalVariable>(
- v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr,
- sem::BindingPoint{});
+ v, ty, sem::EvaluationStage::kOverride, ast::StorageClass::kNone, ast::Access::kUndefined,
+ /* constant_value */ nullptr, sem::BindingPoint{});
if (auto* id = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
sem->SetConstantId(static_cast<uint16_t>(id->value));
@@ -482,11 +483,11 @@
}
auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
- c, ty, ast::StorageClass::kNone, ast::Access::kUndefined, value,
- sem::BindingPoint{}))
+ c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone,
+ ast::Access::kUndefined, value, sem::BindingPoint{}))
: static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
- c, ty, ast::StorageClass::kNone, ast::Access::kUndefined,
- current_statement_, value));
+ c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone,
+ ast::Access::kUndefined, current_statement_, value));
sem->SetConstructor(rhs);
builder_->Sem().Add(c, sem);
@@ -567,12 +568,14 @@
if (auto bp = var->BindingPoint()) {
binding_point = {bp.group->value, bp.binding->value};
}
- sem = builder_->create<sem::GlobalVariable>(var, var_ty, storage_class, access,
+ sem = builder_->create<sem::GlobalVariable>(var, var_ty, sem::EvaluationStage::kRuntime,
+ storage_class, access,
/* constant_value */ nullptr, binding_point);
} else {
- sem = builder_->create<sem::LocalVariable>(
- var, var_ty, storage_class, access, current_statement_, /* constant_value */ nullptr);
+ sem = builder_->create<sem::LocalVariable>(var, var_ty, sem::EvaluationStage::kRuntime,
+ storage_class, access, current_statement_,
+ /* constant_value */ nullptr);
}
sem->SetConstructor(rhs);
@@ -1270,6 +1273,7 @@
[&](const ast::UnaryOpExpression* unary) -> sem::Expression* { return UnaryOp(unary); },
[&](const ast::PhonyExpression*) -> sem::Expression* {
return builder_->create<sem::Expression>(expr, builder_->create<sem::Void>(),
+ sem::EvaluationStage::kRuntime,
current_statement_,
/* constant_value */ nullptr,
/* has_side_effects */ false);
@@ -1423,10 +1427,11 @@
ty = builder_->create<sem::Reference>(ty, ref->StorageClass(), ref->Access());
}
+ auto stage = sem::EarliestStage(obj->Stage(), idx->Stage());
auto val = const_eval_.Index(obj, idx);
bool has_side_effects = idx->HasSideEffects() || obj->HasSideEffects();
auto* sem = builder_->create<sem::IndexAccessorExpression>(
- expr, ty, obj, idx, current_statement_, std::move(val), has_side_effects,
+ expr, ty, stage, obj, idx, current_statement_, std::move(val), has_side_effects,
obj->SourceVariable());
sem->Behaviors() = idx->Behaviors() + obj->Behaviors();
return sem;
@@ -1443,8 +1448,9 @@
}
auto val = const_eval_.Bitcast(ty, inner);
- auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_, std::move(val),
- inner->HasSideEffects());
+ auto stage = sem::EvaluationStage::kRuntime; // TODO(crbug.com/tint/1581)
+ auto* sem = builder_->create<sem::Expression>(expr, ty, stage, current_statement_,
+ std::move(val), inner->HasSideEffects());
sem->Behaviors() = inner->Behaviors();
@@ -1464,6 +1470,7 @@
// Resolve all of the arguments, their types and the set of behaviors.
std::vector<const sem::Expression*> args(expr->args.size());
+ auto args_stage = sem::EvaluationStage::kConstant;
sem::Behaviors arg_behaviors;
for (size_t i = 0; i < expr->args.size(); i++) {
auto* arg = sem_.Get(expr->args[i]);
@@ -1471,6 +1478,7 @@
return nullptr;
}
args[i] = arg;
+ args_stage = sem::EarliestStage(args_stage, arg->Stage());
arg_behaviors.Add(arg->Behaviors());
}
arg_behaviors.Remove(sem::Behavior::kNext);
@@ -1491,14 +1499,38 @@
return nullptr;
}
const sem::Constant* value = nullptr;
- if (ctor_or_conv.const_eval_fn) {
+ auto stage = sem::EarliestStage(ctor_or_conv.target->Stage(), args_stage);
+ if (stage == sem::EvaluationStage::kConstant) {
value = (const_eval_.*ctor_or_conv.const_eval_fn)(ctor_or_conv.target->ReturnType(),
args.data(), args.size());
}
- return builder_->create<sem::Call>(expr, ctor_or_conv.target, std::move(args),
+ return builder_->create<sem::Call>(expr, ctor_or_conv.target, stage, std::move(args),
current_statement_, value, has_side_effects);
};
+ // ct_ctor_or_conv is a helper for building a sem::TypeConstructor for an array or structure
+ // constructor call target.
+ auto arr_or_str_ctor = [&](const sem::Type* ty,
+ const sem::CallTarget* call_target) -> sem::Call* {
+ if (!MaterializeArguments(args, call_target)) {
+ return nullptr;
+ }
+
+ auto stage = args_stage; // The evaluation stage of the call
+ const sem::Constant* value = nullptr; // The constant value for the call
+ if (stage == sem::EvaluationStage::kConstant) {
+ value = const_eval_.ArrayOrStructCtor(ty, args);
+ if (!value) {
+ // Constant evaluation failed.
+ // Can happen for expressions that will fail validation (later).
+ stage = sem::EvaluationStage::kRuntime;
+ }
+ }
+
+ return builder_->create<sem::Call>(expr, call_target, stage, std::move(args),
+ current_statement_, std::move(value), has_side_effects);
+ };
+
// ct_ctor_or_conv is a helper for building either a sem::TypeConstructor or sem::TypeConversion
// call for the given semantic type.
auto ty_ctor_or_conv = [&](const sem::Type* ty) {
@@ -1517,7 +1549,7 @@
[&](const sem::Bool*) { return ct_ctor_or_conv(CtorConvIntrinsic::kBool, nullptr); },
[&](const sem::Array* arr) -> sem::Call* {
auto* call_target = utils::GetOrCreate(
- array_ctors_, ArrayConstructorSig{{arr, args.size()}},
+ array_ctors_, ArrayConstructorSig{{arr, args.size(), args_stage}},
[&]() -> sem::TypeConstructor* {
sem::ParameterList params(args.size());
for (size_t i = 0; i < args.size(); i++) {
@@ -1528,18 +1560,14 @@
ast::StorageClass::kNone, // storage_class
ast::Access::kUndefined); // access
}
- return builder_->create<sem::TypeConstructor>(arr, std::move(params));
+ return builder_->create<sem::TypeConstructor>(arr, std::move(params),
+ args_stage);
});
- if (!MaterializeArguments(args, call_target)) {
- return nullptr;
- }
- auto val = const_eval_.ArrayOrStructCtor(arr, args);
- return builder_->create<sem::Call>(expr, call_target, std::move(args),
- current_statement_, val, has_side_effects);
+ return arr_or_str_ctor(arr, call_target);
},
[&](const sem::Struct* str) -> sem::Call* {
auto* call_target = utils::GetOrCreate(
- struct_ctors_, StructConstructorSig{{str, args.size()}},
+ struct_ctors_, StructConstructorSig{{str, args.size(), args_stage}},
[&]() -> sem::TypeConstructor* {
sem::ParameterList params(std::min(args.size(), str->Members().size()));
for (size_t i = 0, n = params.size(); i < n; i++) {
@@ -1550,15 +1578,10 @@
ast::StorageClass::kNone, // storage_class
ast::Access::kUndefined); // access
}
- return builder_->create<sem::TypeConstructor>(str, std::move(params));
+ return builder_->create<sem::TypeConstructor>(str, std::move(params),
+ args_stage);
});
- if (!MaterializeArguments(args, call_target)) {
- return nullptr;
- }
- auto val = const_eval_.ArrayOrStructCtor(str, args);
- return builder_->create<sem::Call>(expr, call_target, std::move(args),
- current_statement_, std::move(val),
- has_side_effects);
+ return arr_or_str_ctor(str, call_target);
},
[&](Default) {
AddError("type is not constructible", expr->source);
@@ -1686,9 +1709,16 @@
AddWarning("use of deprecated builtin", expr->source);
}
+ auto stage = builtin.sem->Stage();
+ if (stage == sem::EvaluationStage::kConstant) {
+ for (auto* arg : args) {
+ stage = sem::EarliestStage(stage, arg->Stage());
+ }
+ }
+
// If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
const sem::Constant* value = nullptr;
- if (builtin.const_eval_fn) {
+ if (stage == sem::EvaluationStage::kConstant && builtin.const_eval_fn) {
value = (const_eval_.*builtin.const_eval_fn)(builtin.sem->ReturnType(), args.data(),
args.size());
}
@@ -1696,8 +1726,8 @@
bool has_side_effects =
builtin.sem->HasSideEffects() ||
std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
- auto* call = builder_->create<sem::Call>(expr, builtin.sem, std::move(args), current_statement_,
- value, has_side_effects);
+ auto* call = builder_->create<sem::Call>(expr, builtin.sem, stage, std::move(args),
+ current_statement_, value, has_side_effects);
if (current_function_) {
current_function_->AddDirectlyCalledBuiltin(builtin.sem);
@@ -1755,7 +1785,8 @@
// TODO(crbug.com/tint/1420): For now, assume all function calls have side
// effects.
bool has_side_effects = true;
- auto* call = builder_->create<sem::Call>(expr, target, std::move(args), current_statement_,
+ auto* call = builder_->create<sem::Call>(expr, target, sem::EvaluationStage::kRuntime,
+ std::move(args), current_statement_,
/* constant_value */ nullptr, has_side_effects);
target->AddCallSite(call);
@@ -1851,7 +1882,8 @@
}
auto val = const_eval_.Literal(ty, literal);
- return builder_->create<sem::Expression>(literal, ty, current_statement_, std::move(val),
+ return builder_->create<sem::Expression>(literal, ty, sem::EvaluationStage::kConstant,
+ current_statement_, std::move(val),
/* has_side_effects */ false);
}
@@ -2054,6 +2086,7 @@
const auto* rhs = sem_.Get(expr->rhs);
auto* lhs_ty = lhs->Type()->UnwrapRef();
auto* rhs_ty = rhs->Type()->UnwrapRef();
+ auto stage = sem::EvaluationStage::kRuntime; // TODO(crbug.com/tint/1581)
auto op = intrinsic_table_->Lookup(expr->op, lhs_ty, rhs_ty, expr->source, false);
if (!op.result) {
@@ -2079,7 +2112,7 @@
}
bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
- auto* sem = builder_->create<sem::Expression>(expr, op.result, current_statement_, value,
+ auto* sem = builder_->create<sem::Expression>(expr, op.result, stage, current_statement_, value,
has_side_effects);
sem->Behaviors() = lhs->Behaviors() + rhs->Behaviors();
@@ -2096,6 +2129,7 @@
const sem::Type* ty = nullptr;
const sem::Variable* source_var = nullptr;
const sem::Constant* value = nullptr;
+ auto stage = sem::EvaluationStage::kRuntime; // TODO(crbug.com/tint/1581)
switch (unary->op) {
case ast::UnaryOp::kAddressOf:
@@ -2155,7 +2189,7 @@
}
}
- auto* sem = builder_->create<sem::Expression>(unary, ty, current_statement_, value,
+ auto* sem = builder_->create<sem::Expression>(unary, ty, stage, current_statement_, value,
expr->HasSideEffects(), source_var);
sem->Behaviors() = expr->Behaviors();
return sem;