Commit ece9156c authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] allow qualified access to different modules/namespaces

This introduces a new syntax for identifiers and calls: modulename::foo.
Such a name is resolved by trying to find a module modulename in one of
the parent scopes and looking for foo there. So this roughly corresponds
to C++ qualified namespace lookup.

Bug: v8:7793
Change-Id: Iedc43e6ebe125cd74575cbbcbf990bbcc0155a1f
Reviewed-on: https://chromium-review.googlesource.com/c/1309818
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarDaniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57238}
parent 75da45ff
......@@ -186,28 +186,31 @@ class Ast {
struct IdentifierExpression : LocationExpression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(IdentifierExpression)
IdentifierExpression(SourcePosition pos, std::string name,
std::vector<TypeExpression*> args = {})
IdentifierExpression(SourcePosition pos,
std::vector<std::string> namespace_qualification,
std::string name, std::vector<TypeExpression*> args = {})
: LocationExpression(kKind, pos),
namespace_qualification(std::move(namespace_qualification)),
name(std::move(name)),
generic_arguments(std::move(args)) {}
IdentifierExpression(SourcePosition pos, std::string name,
std::vector<TypeExpression*> args = {})
: IdentifierExpression(pos, {}, std::move(name), std::move(args)) {}
std::vector<std::string> namespace_qualification;
std::string name;
std::vector<TypeExpression*> generic_arguments;
};
struct CallExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(CallExpression)
CallExpression(SourcePosition pos, std::string callee, bool is_operator,
std::vector<TypeExpression*> generic_arguments,
CallExpression(SourcePosition pos, IdentifierExpression* callee,
std::vector<Expression*> arguments,
std::vector<std::string> labels)
: Expression(kKind, pos),
callee(pos, std::move(callee), std::move(generic_arguments)),
is_operator(is_operator),
callee(callee),
arguments(std::move(arguments)),
labels(std::move(labels)) {}
IdentifierExpression callee;
bool is_operator;
IdentifierExpression* callee;
std::vector<Expression*> arguments;
std::vector<std::string> labels;
};
......
......@@ -13,6 +13,18 @@ namespace torque {
DEFINE_CONTEXTUAL_VARIABLE(CurrentScope);
std::ostream& operator<<(std::ostream& os, const QualifiedName& name) {
bool first = true;
for (const std::string& qualifier : name.namespace_qualification) {
if (!first) {
os << "::";
}
os << qualifier;
first = false;
}
return os << name.name;
}
std::ostream& operator<<(std::ostream& os, const Callable& m) {
os << "callable " << m.ReadableName() << "(";
if (m.signature().implicit_count != 0) {
......
......@@ -23,6 +23,20 @@ class Scope;
DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*);
struct QualifiedName {
std::vector<std::string> namespace_qualification;
std::string name;
QualifiedName(std::vector<std::string> namespace_qualification,
std::string name)
: namespace_qualification(std::move(namespace_qualification)),
name(std::move(name)) {}
explicit QualifiedName(std::string name)
: QualifiedName({}, std::move(name)) {}
friend std::ostream& operator<<(std::ostream& os, const QualifiedName& name);
};
class Declarable {
public:
virtual ~Declarable() = default;
......@@ -46,6 +60,7 @@ class Declarable {
bool IsExternConstant() const { return kind() == kExternConstant; }
bool IsModuleConstant() const { return kind() == kModuleConstant; }
bool IsValue() const { return IsExternConstant() || IsModuleConstant(); }
bool IsScope() const { return IsModule() || IsCallable(); }
bool IsCallable() const {
return IsMacro() || IsBuiltin() || IsRuntimeFunction();
}
......@@ -85,18 +100,35 @@ class Declarable {
class Scope : public Declarable {
public:
DECLARE_DECLARABLE_BOILERPLATE(Scope, scope);
explicit Scope(Declarable::Kind kind) : Declarable(kind) {}
const std::vector<Declarable*>& LookupShallow(const std::string& name) {
return declarations_[name];
std::vector<Declarable*> LookupShallow(const QualifiedName& name) {
if (name.namespace_qualification.empty()) return declarations_[name.name];
Scope* child = nullptr;
for (Declarable* declarable :
declarations_[name.namespace_qualification.front()]) {
if (Scope* scope = Scope::DynamicCast(declarable)) {
if (child != nullptr) {
ReportError("ambiguous reference to scope ",
name.namespace_qualification.front());
}
child = scope;
}
}
if (child == nullptr) return {};
return child->LookupShallow(
QualifiedName({name.namespace_qualification.begin() + 1,
name.namespace_qualification.end()},
name.name));
}
std::vector<Declarable*> Lookup(const std::string& name) {
std::vector<Declarable*> Lookup(const QualifiedName& name) {
std::vector<Declarable*> result;
if (ParentScope()) {
result = ParentScope()->Lookup(name);
}
for (Declarable* declarable : declarations_[name]) {
for (Declarable* declarable : LookupShallow(name)) {
result.push_back(declarable);
}
return result;
......
......@@ -29,8 +29,8 @@ class DeclarationVisitor : public FileVisitor {
void Visit(Declaration* decl);
Module* GetOrCreateModule(const std::string& name) {
std::vector<Module*> existing_modules =
FilterDeclarables<Module>(Declarations::TryLookupShallow(name));
std::vector<Module*> existing_modules = FilterDeclarables<Module>(
Declarations::TryLookupShallow(QualifiedName(name)));
if (existing_modules.empty()) {
return Declarations::DeclareModule(name);
}
......
......@@ -24,9 +24,8 @@ std::vector<T> EnsureNonempty(std::vector<T> list, const std::string& name,
return std::move(list);
}
template <class T>
T EnsureUnique(const std::vector<T>& list, const std::string& name,
const char* kind) {
template <class T, class Name>
T EnsureUnique(const std::vector<T>& list, const Name& name, const char* kind) {
if (list.empty()) {
ReportError("there is no ", kind, "named ", name);
}
......@@ -39,7 +38,7 @@ T EnsureUnique(const std::vector<T>& list, const std::string& name,
template <class T>
void CheckAlreadyDeclared(const std::string& name, const char* new_type) {
std::vector<T*> declarations =
FilterDeclarables<T>(Declarations::TryLookupShallow(name));
FilterDeclarables<T>(Declarations::TryLookupShallow(QualifiedName(name)));
if (!declarations.empty()) {
Scope* scope = CurrentScope::Get();
ReportError("cannot redeclare ", name, " (type ", new_type, scope, ")");
......@@ -50,7 +49,8 @@ void CheckAlreadyDeclared(const std::string& name, const char* new_type) {
std::vector<Declarable*> Declarations::LookupGlobalScope(
const std::string& name) {
std::vector<Declarable*> d = GlobalContext::GetDefaultModule()->Lookup(name);
std::vector<Declarable*> d =
GlobalContext::GetDefaultModule()->Lookup(QualifiedName(name));
if (d.empty()) {
std::stringstream s;
s << "cannot find \"" << name << "\" in global scope";
......@@ -60,8 +60,8 @@ std::vector<Declarable*> Declarations::LookupGlobalScope(
}
const Type* Declarations::LookupType(const std::string& name) {
TypeAlias* declaration =
EnsureUnique(FilterDeclarables<TypeAlias>(Lookup(name)), name, "type");
TypeAlias* declaration = EnsureUnique(
FilterDeclarables<TypeAlias>(Lookup(QualifiedName(name))), name, "type");
return declaration->type();
}
......@@ -105,13 +105,13 @@ Builtin* Declarations::FindSomeInternalBuiltinWithType(
return nullptr;
}
Value* Declarations::LookupValue(const std::string& name) {
Value* Declarations::LookupValue(const QualifiedName& name) {
return EnsureUnique(FilterDeclarables<Value>(Lookup(name)), name, "value");
}
Macro* Declarations::TryLookupMacro(const std::string& name,
const TypeVector& types) {
std::vector<Macro*> macros = TryLookup<Macro>(name);
std::vector<Macro*> macros = TryLookup<Macro>(QualifiedName(name));
for (auto& m : macros) {
auto signature_types = m->signature().GetExplicitTypes();
if (signature_types == types && !m->signature().parameter_types.var_args) {
......@@ -122,18 +122,18 @@ Macro* Declarations::TryLookupMacro(const std::string& name,
}
base::Optional<Builtin*> Declarations::TryLookupBuiltin(
const std::string& name) {
const QualifiedName& name) {
std::vector<Builtin*> builtins = TryLookup<Builtin>(name);
if (builtins.empty()) return base::nullopt;
return EnsureUnique(builtins, name, "builtin");
return EnsureUnique(builtins, name.name, "builtin");
}
std::vector<Generic*> Declarations::LookupGeneric(const std::string& name) {
return EnsureNonempty(FilterDeclarables<Generic>(Lookup(name)), name,
"generic");
return EnsureNonempty(FilterDeclarables<Generic>(Lookup(QualifiedName(name))),
name, "generic");
}
Generic* Declarations::LookupUniqueGeneric(const std::string& name) {
Generic* Declarations::LookupUniqueGeneric(const QualifiedName& name) {
return EnsureUnique(FilterDeclarables<Generic>(Lookup(name)), name,
"generic");
}
......
......@@ -31,20 +31,20 @@ std::vector<T*> FilterDeclarables(const std::vector<Declarable*> list) {
class Declarations {
public:
static std::vector<Declarable*> TryLookup(const std::string& name) {
static std::vector<Declarable*> TryLookup(const QualifiedName& name) {
return CurrentScope::Get()->Lookup(name);
}
static std::vector<Declarable*> TryLookupShallow(const std::string& name) {
static std::vector<Declarable*> TryLookupShallow(const QualifiedName& name) {
return CurrentScope::Get()->LookupShallow(name);
}
template <class T>
static std::vector<T*> TryLookup(const std::string& name) {
static std::vector<T*> TryLookup(const QualifiedName& name) {
return FilterDeclarables<T>(TryLookup(name));
}
static std::vector<Declarable*> Lookup(const std::string& name) {
static std::vector<Declarable*> Lookup(const QualifiedName& name) {
std::vector<Declarable*> d = TryLookup(name);
if (d.empty()) {
std::stringstream s;
......@@ -63,14 +63,14 @@ class Declarations {
static Builtin* FindSomeInternalBuiltinWithType(
const FunctionPointerType* type);
static Value* LookupValue(const std::string& name);
static Value* LookupValue(const QualifiedName& name);
static Macro* TryLookupMacro(const std::string& name,
const TypeVector& types);
static base::Optional<Builtin*> TryLookupBuiltin(const std::string& name);
static base::Optional<Builtin*> TryLookupBuiltin(const QualifiedName& name);
static std::vector<Generic*> LookupGeneric(const std::string& name);
static Generic* LookupUniqueGeneric(const std::string& name);
static Generic* LookupUniqueGeneric(const QualifiedName& name);
static Module* DeclareModule(const std::string& name);
......
......@@ -1242,7 +1242,7 @@ void ImplementationVisitor::GenerateFunctionDeclaration(
namespace {
void FailCallableLookup(const std::string& reason, const std::string& name,
void FailCallableLookup(const std::string& reason, const QualifiedName& name,
const Arguments& arguments,
const std::vector<Signature>& candidates) {
std::stringstream stream;
......@@ -1302,7 +1302,7 @@ Block* ImplementationVisitor::LookupSimpleLabel(const std::string& name) {
}
Callable* ImplementationVisitor::LookupCall(
const std::string& name, const Arguments& arguments,
const QualifiedName& name, const Arguments& arguments,
const TypeVector& specialization_types) {
Callable* result = nullptr;
TypeVector parameter_types(arguments.parameters.GetTypeVector());
......@@ -1486,19 +1486,22 @@ LocationReference ImplementationVisitor::GetLocationReference(
LocationReference ImplementationVisitor::GetLocationReference(
IdentifierExpression* expr) {
if (base::Optional<Binding<LocalValue>*> value =
TryLookupLocalValue(expr->name)) {
if (expr->generic_arguments.size() != 0) {
ReportError("cannot have generic parameters on local name ", expr->name);
}
if ((*value)->is_const) {
return LocationReference::Temporary((*value)->value,
"constant value " + expr->name);
if (expr->namespace_qualification.empty()) {
if (base::Optional<Binding<LocalValue>*> value =
TryLookupLocalValue(expr->name)) {
if (expr->generic_arguments.size() != 0) {
ReportError("cannot have generic parameters on local name ",
expr->name);
}
if ((*value)->is_const) {
return LocationReference::Temporary((*value)->value,
"constant value " + expr->name);
}
return LocationReference::VariableAccess((*value)->value);
}
return LocationReference::VariableAccess((*value)->value);
}
std::string name = expr->name;
QualifiedName name = QualifiedName(expr->namespace_qualification, expr->name);
if (base::Optional<Builtin*> builtin = Declarations::TryLookupBuiltin(name)) {
return LocationReference::Temporary(GetBuiltinCode(*builtin),
"builtin " + expr->name);
......@@ -1516,7 +1519,7 @@ LocationReference ImplementationVisitor::GetLocationReference(
generic->name());
}
}
Value* value = Declarations::LookupValue(expr->name);
Value* value = Declarations::LookupValue(name);
if (auto* constant = ModuleConstant::DynamicCast(value)) {
if (constant->type()->IsConstexpr()) {
return LocationReference::Temporary(
......@@ -1620,7 +1623,7 @@ VisitResult ImplementationVisitor::GeneratePointerCall(
}
VisitResult ImplementationVisitor::GenerateCall(
const std::string& callable_name, Arguments arguments,
const QualifiedName& callable_name, Arguments arguments,
const TypeVector& specialization_types, bool is_tailcall) {
Callable* callable =
LookupCall(callable_name, arguments, specialization_types);
......@@ -1647,8 +1650,8 @@ VisitResult ImplementationVisitor::GenerateCall(
base::Optional<Binding<LocalValue>*> val =
TryLookupLocalValue(implicit_name);
if (!val) {
ReportError("implicit parameter '" + implicit_name +
"' required for call to '" + callable_name +
ReportError("implicit parameter '", implicit_name,
"' required for call to '", callable_name,
"' is not defined");
}
VisitResult converted = GenerateImplicitConvert(
......@@ -1821,17 +1824,19 @@ VisitResult ImplementationVisitor::Visit(CallExpression* expr,
bool is_tailcall) {
StackScope scope(this);
Arguments arguments;
std::string name = expr->callee.name;
QualifiedName name =
QualifiedName(expr->callee->namespace_qualification, expr->callee->name);
TypeVector specialization_types =
GetTypeVector(expr->callee.generic_arguments);
GetTypeVector(expr->callee->generic_arguments);
bool has_template_arguments = !specialization_types.empty();
for (Expression* arg : expr->arguments)
arguments.parameters.push_back(Visit(arg));
arguments.labels = LabelsFromIdentifiers(expr->labels);
VisitResult result;
if (!has_template_arguments && TryLookupLocalValue(expr->callee.name)) {
if (!has_template_arguments && name.namespace_qualification.empty() &&
TryLookupLocalValue(name.name)) {
return scope.Yield(
GeneratePointerCall(&expr->callee, arguments, is_tailcall));
GeneratePointerCall(expr->callee, arguments, is_tailcall));
} else {
return scope.Yield(
GenerateCall(name, arguments, specialization_types, is_tailcall));
......
......@@ -368,7 +368,7 @@ class ImplementationVisitor : public FileVisitor {
base::Optional<Binding<LocalLabel>*> TryLookupLabel(const std::string& name);
Binding<LocalLabel>* LookupLabel(const std::string& name);
Block* LookupSimpleLabel(const std::string& name);
Callable* LookupCall(const std::string& name, const Arguments& arguments,
Callable* LookupCall(const QualifiedName& name, const Arguments& arguments,
const TypeVector& specialization_types);
const Type* GetCommonType(const Type* left, const Type* right);
......@@ -378,10 +378,16 @@ class ImplementationVisitor : public FileVisitor {
void GenerateAssignToLocation(const LocationReference& reference,
const VisitResult& assignment_value);
VisitResult GenerateCall(const std::string& callable_name,
VisitResult GenerateCall(const QualifiedName& callable_name,
Arguments parameters,
const TypeVector& specialization_types = {},
bool tail_call = false);
VisitResult GenerateCall(std::string callable_name, Arguments parameters,
const TypeVector& specialization_types = {},
bool tail_call = false) {
return GenerateCall(QualifiedName(std::move(callable_name)),
std::move(parameters), specialization_types, tail_call);
}
VisitResult GeneratePointerCall(Expression* callee,
const Arguments& parameters, bool tail_call);
......
......@@ -207,8 +207,7 @@ void CheckNotDeferredStatement(Statement* statement) {
}
}
Expression* MakeCall(const std::string& callee, bool is_operator,
const std::vector<TypeExpression*>& generic_arguments,
Expression* MakeCall(IdentifierExpression* callee,
const std::vector<Expression*>& arguments,
const std::vector<Statement*>& otherwise) {
std::vector<std::string> labels;
......@@ -237,20 +236,27 @@ Expression* MakeCall(const std::string& callee, bool is_operator,
// Create nested try-label expression for all of the temporary Labels that
// were created.
Expression* result = MakeNode<CallExpression>(
callee, false, generic_arguments, arguments, labels);
Expression* result = MakeNode<CallExpression>(callee, arguments, labels);
for (auto* label : temp_labels) {
result = MakeNode<TryLabelExpression>(false, result, label);
}
return result;
}
Expression* MakeCall(const std::string& callee,
const std::vector<TypeExpression*>& generic_arguments,
const std::vector<Expression*>& arguments,
const std::vector<Statement*>& otherwise) {
return MakeCall(MakeNode<IdentifierExpression>(callee, generic_arguments),
arguments, otherwise);
}
base::Optional<ParseResult> MakeCall(ParseResultIterator* child_results) {
auto callee = child_results->NextAs<std::string>();
auto generic_args = child_results->NextAs<TypeList>();
auto callee = child_results->NextAs<LocationExpression*>();
auto args = child_results->NextAs<std::vector<Expression*>>();
auto otherwise = child_results->NextAs<std::vector<Statement*>>();
return ParseResult{MakeCall(callee, false, generic_args, args, otherwise)};
return ParseResult{
MakeCall(IdentifierExpression::cast(callee), args, otherwise)};
}
base::Optional<ParseResult> MakeBinaryOperator(
......@@ -258,7 +264,7 @@ base::Optional<ParseResult> MakeBinaryOperator(
auto left = child_results->NextAs<Expression*>();
auto op = child_results->NextAs<std::string>();
auto right = child_results->NextAs<Expression*>();
return ParseResult{MakeCall(op, true, TypeList{},
return ParseResult{MakeCall(op, TypeList{},
std::vector<Expression*>{left, right},
std::vector<Statement*>{})};
}
......@@ -267,7 +273,7 @@ base::Optional<ParseResult> MakeUnaryOperator(
ParseResultIterator* child_results) {
auto op = child_results->NextAs<std::string>();
auto e = child_results->NextAs<Expression*>();
return ParseResult{MakeCall(op, true, TypeList{}, std::vector<Expression*>{e},
return ParseResult{MakeCall(op, TypeList{}, std::vector<Expression*>{e},
std::vector<Statement*>{})};
}
......@@ -662,11 +668,10 @@ base::Optional<ParseResult> MakeTypeswitchStatement(
}
BlockStatement* case_block;
if (i < cases.size() - 1) {
value =
MakeCall("Cast", false, std::vector<TypeExpression*>{cases[i].type},
std::vector<Expression*>{value},
std::vector<Statement*>{MakeNode<ExpressionStatement>(
MakeNode<IdentifierExpression>("_NextCase"))});
value = MakeCall("Cast", std::vector<TypeExpression*>{cases[i].type},
std::vector<Expression*>{value},
std::vector<Statement*>{MakeNode<ExpressionStatement>(
MakeNode<IdentifierExpression>("_NextCase"))});
case_block = MakeNode<BlockStatement>();
} else {
case_block = current_block;
......@@ -864,11 +869,14 @@ base::Optional<ParseResult> MakeExpressionWithSource(
base::Optional<ParseResult> MakeIdentifierExpression(
ParseResultIterator* child_results) {
auto namespace_qualification =
child_results->NextAs<std::vector<std::string>>();
auto name = child_results->NextAs<std::string>();
auto generic_arguments =
child_results->NextAs<std::vector<TypeExpression*>>();
LocationExpression* result = MakeNode<IdentifierExpression>(
std::move(name), std::move(generic_arguments));
std::move(namespace_qualification), std::move(name),
std::move(generic_arguments));
return ParseResult{result};
}
......@@ -1197,10 +1205,16 @@ struct TorqueGrammar : Grammar {
IncrementDecrementOperator::kDecrement>)};
// Result: LocationExpression*
Symbol locationExpression = {
Symbol identifierExpression = {
Rule(
{&identifier, TryOrDefault<TypeList>(&genericSpecializationTypeList)},
{List<std::string>(Sequence({&identifier, Token("::")})), &identifier,
TryOrDefault<TypeList>(&genericSpecializationTypeList)},
MakeIdentifierExpression),
};
// Result: LocationExpression*
Symbol locationExpression = {
Rule({&identifierExpression}),
Rule({&primaryExpression, Token("."), &identifier},
MakeFieldAccessExpression),
Rule({&primaryExpression, Token("["), expression, Token("]")},
......@@ -1211,10 +1225,8 @@ struct TorqueGrammar : Grammar {
{Token("("), List<Expression*>(expression, Token(",")), Token(")")})};
// Result: Expression*
Symbol callExpression = {
Rule({&identifier, TryOrDefault<TypeList>(&genericSpecializationTypeList),
&argumentList, optionalOtherwise},
MakeCall)};
Symbol callExpression = {Rule(
{&identifierExpression, &argumentList, optionalOtherwise}, MakeCall)};
// Result: Expression*
Symbol primaryExpression = {
......
......@@ -343,6 +343,18 @@ TEST(TestCatch3) {
ft.Call();
}
TEST(TestLookup) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeAssemblerTester asm_tester(isolate, 0);
TestBuiltinsFromDSLAssembler m(asm_tester.state());
{
m.TestQualifiedAccess();
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -599,6 +599,11 @@ module test {
}
}
macro TestQualifiedAccess() {
let s: Smi = 0;
check(!array::IsJSArray(s));
}
macro TestCatch1(context: Context): Smi {
let r: Smi = 0;
try {
......@@ -641,29 +646,25 @@ module test {
return r;
}
}
}
// Until we fully support namespaces, put the test for iterators in the
// iterator module so that the macros and builtins are found.
module iterator {
// This test doesn't actually test the functionality of iterators,
// it's only purpose is to make sure tha the CSA macros in the
// IteratorBuiltinsAssembler match the signatures provided in
// iterator.tq.
macro TestIterator(implicit context: Context)(o: Object, map: Map) {
try {
const t1: Object = GetIteratorMethod(o);
const t2: IteratorRecord = GetIterator(o);
const t1: Object = iterator::GetIteratorMethod(o);
const t2: IteratorRecord = iterator::GetIterator(o);
const t3: Object = IteratorStep(t2) otherwise Fail;
const t4: Object = IteratorStep(t2, map) otherwise Fail;
const t3: Object = iterator::IteratorStep(t2) otherwise Fail;
const t4: Object = iterator::IteratorStep(t2, map) otherwise Fail;
const t5: Object = IteratorValue(t4);
const t6: Object = IteratorValue(t4, map);
const t5: Object = iterator::IteratorValue(t4);
const t6: Object = iterator::IteratorValue(t4, map);
IteratorCloseOnException(t2);
iterator::IteratorCloseOnException(t2);
const t7: JSArray = IterableToList(t1, t1);
const t7: JSArray = iterator::IterableToList(t1, t1);
}
label Fail {}
}
......
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