[torque] Unused implicit parameters can be undefined

e.g. the following is now valid Torque code:

  macro TestA(implicit c: Context)() {}

  macro TestB(): bool {
    return TestA();
  }

This is handy for more flexible usage of generics that may or may not
use implicit parameters deep inside their specializations.

Note that this change doesn't change the fundamental rigor (or lack
thereof) around checking the usage of implicit parameters, which
already do not require '_' before their parameter identifier if
unused. It just silences errors in cases where a call site doesn't
implicitly pass a parameter that ultimately doesn't have a use site
and adds meaningful error messages in the case that it does.

Bug: v8:7793
Change-Id: I559d06c0864a7e79fe52bee5a9a7af9941889748
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2274127
Commit-Queue: Daniel Clifford <danno@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68618}
This commit is contained in:
Daniel Clifford 2020-06-30 17:55:16 +02:00 committed by Commit Bot
parent 0d3e5872a1
commit 9bd8e5f247
6 changed files with 108 additions and 19 deletions

View File

@ -95,8 +95,12 @@ std::vector<Declarable*> Scope::Lookup(const QualifiedName& name) {
base::Optional<std::string> TypeConstraint::IsViolated(const Type* type) const {
if (upper_bound && !type->IsSubtypeOf(*upper_bound)) {
return {
ToString("expected ", *type, " to be a subtype of ", **upper_bound)};
if (type->IsTopType()) {
return TopType::cast(type)->reason();
} else {
return {
ToString("expected ", *type, " to be a subtype of ", **upper_bound)};
}
}
return base::nullopt;
}

View File

@ -2391,10 +2391,16 @@ VisitResult ImplementationVisitor::GeneratePointerCall(
void ImplementationVisitor::AddCallParameter(
Callable* callable, VisitResult parameter, const Type* parameter_type,
std::vector<VisitResult>* converted_arguments, StackRange* argument_range,
std::vector<std::string>* constexpr_arguments) {
VisitResult converted = GenerateImplicitConvert(parameter_type, parameter);
std::vector<std::string>* constexpr_arguments, bool inline_macro) {
VisitResult converted;
if ((converted_arguments->size() < callable->signature().implicit_count) &&
parameter.type()->IsTopType()) {
converted = GenerateCopy(parameter);
} else {
converted = GenerateImplicitConvert(parameter_type, parameter);
}
converted_arguments->push_back(converted);
if (!callable->ShouldBeInlined()) {
if (!inline_macro) {
if (converted.IsOnStack()) {
argument_range->Extend(converted.stack_range());
} else {
@ -2444,18 +2450,33 @@ VisitResult ImplementationVisitor::GenerateCall(
}
}
bool inline_macro = callable->ShouldBeInlined();
std::vector<VisitResult> implicit_arguments;
for (size_t i = 0; i < callable->signature().implicit_count; ++i) {
std::string implicit_name = callable->signature().parameter_names[i]->value;
base::Optional<Binding<LocalValue>*> val =
TryLookupLocalValue(implicit_name);
if (!val) {
ReportError("implicit parameter '", implicit_name,
"' required for call to '", callable->ReadableName(),
"' is not defined");
if (val) {
implicit_arguments.push_back(
GenerateFetchFromLocation((*val)->GetLocationReference(*val)));
} else {
VisitResult unititialized = VisitResult::TopTypeResult(
"implicit parameter '" + implicit_name +
"' is not defined when invoking " + callable->ReadableName() +
" at " + PositionAsString(CurrentSourcePosition::Get()),
callable->signature().parameter_types.types[i]);
implicit_arguments.push_back(unititialized);
}
const Type* type = implicit_arguments.back().type();
if (const TopType* top_type = TopType::DynamicCast(type)) {
if (!callable->IsMacro() || callable->IsExternal()) {
ReportError(
"unititialized implicit parameters can only be passed to "
"Torque-defined macros: the ",
top_type->reason());
}
inline_macro = true;
}
implicit_arguments.push_back(
GenerateFetchFromLocation((*val)->GetLocationReference(*val)));
}
std::vector<VisitResult> converted_arguments;
@ -2467,7 +2488,7 @@ VisitResult ImplementationVisitor::GenerateCall(
AddCallParameter(callable, implicit_arguments[current],
callable->signature().parameter_types.types[current],
&converted_arguments, &argument_range,
&constexpr_arguments);
&constexpr_arguments, inline_macro);
}
if (this_reference) {
@ -2476,7 +2497,7 @@ VisitResult ImplementationVisitor::GenerateCall(
// By now, the this reference should either be a variable, a temporary or
// a Slice. In either case the fetch of the VisitResult should succeed.
VisitResult this_value = this_reference->GetVisitResult();
if (method->ShouldBeInlined()) {
if (inline_macro) {
if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
ReportError("this parameter must be a subtype of ",
*method->aggregate_type(), " but it is of type ",
@ -2485,7 +2506,7 @@ VisitResult ImplementationVisitor::GenerateCall(
} else {
AddCallParameter(callable, this_value, method->aggregate_type(),
&converted_arguments, &argument_range,
&constexpr_arguments);
&constexpr_arguments, inline_macro);
}
++current;
}
@ -2495,7 +2516,7 @@ VisitResult ImplementationVisitor::GenerateCall(
? TypeOracle::GetObjectType()
: callable->signature().types()[current++];
AddCallParameter(callable, arg, to_type, &converted_arguments,
&argument_range, &constexpr_arguments);
&argument_range, &constexpr_arguments, inline_macro);
}
size_t label_count = callable->signature().labels.size();
@ -2559,7 +2580,7 @@ VisitResult ImplementationVisitor::GenerateCall(
}
result << "))";
return VisitResult(return_type, result.str());
} else if (macro->ShouldBeInlined()) {
} else if (inline_macro) {
std::vector<Block*> label_blocks;
for (Binding<LocalLabel>* label : arguments.labels) {
label_blocks.push_back(label->block);
@ -2874,8 +2895,13 @@ VisitResult ImplementationVisitor::GenerateImplicitConvert(
return scope.Yield(GenerateCopy(source));
} else {
std::stringstream s;
s << "cannot use expression of type " << *source.type()
<< " as a value of type " << *destination_type;
if (const TopType* top_type = TopType::DynamicCast(source.type())) {
s << "undefined expression of type " << *destination_type << ": the "
<< top_type->reason();
} else {
s << "cannot use expression of type " << *source.type()
<< " as a value of type " << *destination_type;
}
ReportError(s.str());
}
}

View File

@ -693,7 +693,8 @@ class ImplementationVisitor {
const Type* parameter_type,
std::vector<VisitResult>* converted_arguments,
StackRange* argument_range,
std::vector<std::string>* constexpr_arguments);
std::vector<std::string>* constexpr_arguments,
bool inline_macro);
VisitResult GenerateCall(Callable* callable,
base::Optional<LocationReference> this_parameter,

View File

@ -912,6 +912,13 @@ VisitResult VisitResult::NeverResult() {
return result;
}
VisitResult VisitResult::TopTypeResult(std::string top_reason,
const Type* from_type) {
VisitResult result;
result.type_ = TypeOracle::GetTopType(std::move(top_reason), from_type);
return result;
}
std::tuple<size_t, std::string> Field::GetFieldSizeInformation() const {
auto optional = SizeOf(this->name_and_type.type);
if (optional.has_value()) {

View File

@ -756,6 +756,8 @@ class VisitResult {
DCHECK(type->IsConstexpr());
}
static VisitResult NeverResult();
static VisitResult TopTypeResult(std::string top_reason,
const Type* from_type);
VisitResult(const Type* type, StackRange stack_range)
: type_(type), stack_range_(stack_range) {
DCHECK(!type->IsConstexpr());

View File

@ -849,6 +849,55 @@ TEST(Torque, FieldAccessOnNonClassType) {
HasSubstr("map"));
}
TEST(Torque, UnusedImplicit) {
ExpectSuccessfulCompilation(R"(
@export
macro Test1(implicit c: Smi)(a: Object): Object { return a; }
@export
macro Test2(b: Object) { Test1(b); }
)");
ExpectFailingCompilation(
R"(
macro Test1(implicit c: Smi)(_a: Object): Smi { return c; }
@export
macro Test2(b: Smi) { Test1(b); }
)",
HasSubstr("undefined expression of type Smi: the implicit "
"parameter 'c' is not defined when invoking Test1 at"));
ExpectFailingCompilation(
R"(
extern macro Test3(implicit c: Smi)(Object): Smi;
@export
macro Test4(b: Smi) { Test3(b); }
)",
HasSubstr("unititialized implicit parameters can only be passed to "
"Torque-defined macros: the implicit parameter 'c' is not "
"defined when invoking Test3"));
ExpectSuccessfulCompilation(
R"(
macro Test7<T: type>(implicit c: Smi)(o: T): Smi;
Test7<Smi>(implicit c: Smi)(o: Smi): Smi { return o; }
@export
macro Test8(b: Smi) { Test7(b); }
)");
ExpectFailingCompilation(
R"(
macro Test6<T: type>(_o: T): T;
macro Test6<T: type>(implicit c: T)(_o: T): T {
return c;
}
macro Test7<T: type>(o: T): Smi;
Test7<Smi>(o: Smi): Smi { return Test6<Smi>(o); }
@export
macro Test8(b: Smi) { Test7(b); }
)",
HasSubstr("\nambiguous callable : \n Test6(Smi)\ncandidates are:\n "
"Test6(Smi): Smi\n Test6(implicit Smi)(Smi): Smi"));
}
} // namespace torque
} // namespace internal
} // namespace v8