Commit 9bd8e5f2 authored by Daniel Clifford's avatar Daniel Clifford Committed by Commit Bot

[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: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68618}
parent 0d3e5872
......@@ -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;
}
......
......@@ -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());
}
}
......
......@@ -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,
......
......@@ -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()) {
......
......@@ -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());
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment