blob: 27edf796368df53b6a2c5f4b77d744ec631f8bab [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TORQUE_DECLARABLE_H_
#define V8_TORQUE_DECLARABLE_H_
#include <cassert>
#include <string>
#include <unordered_map>
#include "src/base/functional.h"
#include "src/base/logging.h"
#include "src/torque/ast.h"
#include "src/torque/types.h"
#include "src/torque/utils.h"
namespace v8 {
namespace internal {
namespace torque {
class Scope;
class Namespace;
class TypeArgumentInference;
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)) {}
bool HasNamespaceQualification() const {
return !namespace_qualification.empty();
}
QualifiedName DropFirstNamespaceQualification() const {
return QualifiedName{
std::vector<std::string>(namespace_qualification.begin() + 1,
namespace_qualification.end()),
name};
}
friend std::ostream& operator<<(std::ostream& os, const QualifiedName& name);
};
class Declarable {
public:
virtual ~Declarable() = default;
enum Kind {
kNamespace,
kTorqueMacro,
kExternMacro,
kMethod,
kBuiltin,
kRuntimeFunction,
kIntrinsic,
kGenericCallable,
kGenericType,
kTypeAlias,
kExternConstant,
kNamespaceConstant
};
Kind kind() const { return kind_; }
bool IsNamespace() const { return kind() == kNamespace; }
bool IsMacro() const { return IsTorqueMacro() || IsExternMacro(); }
bool IsTorqueMacro() const { return kind() == kTorqueMacro || IsMethod(); }
bool IsMethod() const { return kind() == kMethod; }
bool IsExternMacro() const { return kind() == kExternMacro; }
bool IsIntrinsic() const { return kind() == kIntrinsic; }
bool IsBuiltin() const { return kind() == kBuiltin; }
bool IsRuntimeFunction() const { return kind() == kRuntimeFunction; }
bool IsGenericCallable() const { return kind() == kGenericCallable; }
bool IsGenericType() const { return kind() == kGenericType; }
bool IsTypeAlias() const { return kind() == kTypeAlias; }
bool IsExternConstant() const { return kind() == kExternConstant; }
bool IsNamespaceConstant() const { return kind() == kNamespaceConstant; }
bool IsValue() const { return IsExternConstant() || IsNamespaceConstant(); }
bool IsScope() const { return IsNamespace() || IsCallable(); }
bool IsCallable() const {
return IsMacro() || IsBuiltin() || IsRuntimeFunction() || IsIntrinsic() ||
IsMethod();
}
virtual const char* type_name() const { return "<<unknown>>"; }
Scope* ParentScope() const { return parent_scope_; }
// The SourcePosition of the whole declarable. For example, for a macro
// this will encompass not only the signature, but also the body.
SourcePosition Position() const { return position_; }
void SetPosition(const SourcePosition& position) { position_ = position; }
// The SourcePosition of the identifying name of the declarable. For example,
// for a macro this will be the SourcePosition of the name.
// Note that this SourcePosition might not make sense for all kinds of
// declarables, in that case, the default SourcePosition is returned.
SourcePosition IdentifierPosition() const {
return identifier_position_.source.IsValid() ? identifier_position_
: position_;
}
void SetIdentifierPosition(const SourcePosition& position) {
identifier_position_ = position;
}
bool IsUserDefined() const { return is_user_defined_; }
void SetIsUserDefined(bool is_user_defined) {
is_user_defined_ = is_user_defined;
}
protected:
explicit Declarable(Kind kind) : kind_(kind) {}
private:
const Kind kind_;
Scope* const parent_scope_ = CurrentScope::Get();
SourcePosition position_ = CurrentSourcePosition::Get();
SourcePosition identifier_position_ = SourcePosition::Invalid();
bool is_user_defined_ = true;
};
#define DECLARE_DECLARABLE_BOILERPLATE(x, y) \
static x* cast(Declarable* declarable) { \
DCHECK(declarable->Is##x()); \
return static_cast<x*>(declarable); \
} \
static const x* cast(const Declarable* declarable) { \
DCHECK(declarable->Is##x()); \
return static_cast<const x*>(declarable); \
} \
const char* type_name() const override { return #y; } \
static x* DynamicCast(Declarable* declarable) { \
if (!declarable) return nullptr; \
if (!declarable->Is##x()) return nullptr; \
return static_cast<x*>(declarable); \
} \
static const x* DynamicCast(const Declarable* declarable) { \
if (!declarable) return nullptr; \
if (!declarable->Is##x()) return nullptr; \
return static_cast<const x*>(declarable); \
}
// Information about what code caused a specialization to exist. This is used
// for error reporting.
struct SpecializationRequester {
// The position of the expression that caused this specialization.
SourcePosition position;
// The Scope which contains the expression that caused this specialization.
// It may in turn also be within a specialization, which allows us to print
// the stack of requesters when an error occurs.
Scope* scope;
// The name of the specialization.
std::string name;
static SpecializationRequester None() {
return {SourcePosition::Invalid(), nullptr, ""};
}
bool IsNone() const {
return position == SourcePosition::Invalid() && scope == nullptr &&
name == "";
}
SpecializationRequester(SourcePosition position, Scope* scope,
std::string name);
};
class Scope : public Declarable {
public:
DECLARE_DECLARABLE_BOILERPLATE(Scope, scope)
explicit Scope(Declarable::Kind kind) : Declarable(kind) {}
std::vector<Declarable*> LookupShallow(const QualifiedName& name) {
if (!name.HasNamespaceQualification()) 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(name.DropFirstNamespaceQualification());
}
std::vector<Declarable*> Lookup(const QualifiedName& name);
template <class T>
T* AddDeclarable(const std::string& name, T* declarable) {
declarations_[name].push_back(declarable);
return declarable;
}
const SpecializationRequester& GetSpecializationRequester() const {
return requester_;
}
void SetSpecializationRequester(const SpecializationRequester& requester) {
requester_ = requester;
}
private:
std::unordered_map<std::string, std::vector<Declarable*>> declarations_;
// If this Scope was created for specializing a generic type or callable,
// then {requester_} refers to the place that caused the specialization so we
// can construct useful error messages.
SpecializationRequester requester_ = SpecializationRequester::None();
};
class Namespace : public Scope {
public:
DECLARE_DECLARABLE_BOILERPLATE(Namespace, namespace)
explicit Namespace(const std::string& name)
: Scope(Declarable::kNamespace), name_(name) {}
const std::string& name() const { return name_; }
bool IsDefaultNamespace() const;
bool IsTestNamespace() const;
private:
std::string name_;
};
inline Namespace* CurrentNamespace() {
Scope* scope = CurrentScope::Get();
while (true) {
if (Namespace* n = Namespace::DynamicCast(scope)) {
return n;
}
scope = scope->ParentScope();
}
}
class Value : public Declarable {
public:
DECLARE_DECLARABLE_BOILERPLATE(Value, value)
const Identifier* name() const { return name_; }
virtual bool IsConst() const { return true; }
VisitResult value() const { return *value_; }
const Type* type() const { return type_; }
void set_value(VisitResult value) {
DCHECK(!value_);
value_ = value;
}
protected:
Value(Kind kind, const Type* type, Identifier* name)
: Declarable(kind), type_(type), name_(name) {}
private:
const Type* type_;
Identifier* name_;
base::Optional<VisitResult> value_;
};
class NamespaceConstant : public Value {
public:
DECLARE_DECLARABLE_BOILERPLATE(NamespaceConstant, constant)
const std::string& external_name() const { return external_name_; }
Expression* body() const { return body_; }
private:
friend class Declarations;
explicit NamespaceConstant(Identifier* constant_name,
std::string external_name, const Type* type,
Expression* body)
: Value(Declarable::kNamespaceConstant, type, constant_name),
external_name_(std::move(external_name)),
body_(body) {}
std::string external_name_;
Expression* body_;
};
class ExternConstant : public Value {
public:
DECLARE_DECLARABLE_BOILERPLATE(ExternConstant, constant)
private:
friend class Declarations;
explicit ExternConstant(Identifier* name, const Type* type, std::string value)
: Value(Declarable::kExternConstant, type, name) {
set_value(VisitResult(type, std::move(value)));
}
};
enum class OutputType {
kCSA,
kCC,
};
class Callable : public Scope {
public:
DECLARE_DECLARABLE_BOILERPLATE(Callable, callable)
const std::string& ExternalName() const { return external_name_; }
const std::string& ReadableName() const { return readable_name_; }
const Signature& signature() const { return signature_; }
bool IsTransitioning() const { return signature().transitioning; }
const NameVector& parameter_names() const {
return signature_.parameter_names;
}
bool HasReturnValue() const {
return !signature_.return_type->IsVoidOrNever();
}
void IncrementReturns() { ++returns_; }
bool HasReturns() const { return returns_; }
base::Optional<Statement*> body() const { return body_; }
bool IsExternal() const { return !body_.has_value(); }
virtual bool ShouldBeInlined(OutputType output_type) const {
// C++ output doesn't support exiting to labels, so functions with labels in
// the signature must be inlined.
return output_type == OutputType::kCC && !signature().labels.empty();
}
bool ShouldGenerateExternalCode(OutputType output_type) const {
return !ShouldBeInlined(output_type);
}
static std::string PrefixNameForCCOutput(const std::string& name) {
// If a Torque macro requires a C++ runtime function to be generated, then
// the generated function begins with this prefix to avoid any naming
// collisions with the generated CSA function for the same macro.
return "TqRuntime" + name;
}
// Name to use in runtime C++ code.
virtual std::string CCName() const {
return PrefixNameForCCOutput(ExternalName());
}
protected:
Callable(Declarable::Kind kind, std::string external_name,
std::string readable_name, Signature signature,
base::Optional<Statement*> body)
: Scope(kind),
external_name_(std::move(external_name)),
readable_name_(std::move(readable_name)),
signature_(std::move(signature)),
returns_(0),
body_(body) {
DCHECK(!body || *body);
}
private:
std::string external_name_;
std::string readable_name_;
Signature signature_;
size_t returns_;
base::Optional<Statement*> body_;
};
class Macro : public Callable {
public:
DECLARE_DECLARABLE_BOILERPLATE(Macro, macro)
bool ShouldBeInlined(OutputType output_type) const override {
for (const LabelDeclaration& label : signature().labels) {
for (const Type* type : label.types) {
if (type->StructSupertype()) return true;
}
}
// Intrinsics that are used internally in Torque and implemented as torque
// code should be inlined and not generate C++ definitions.
if (ReadableName()[0] == '%') return true;
return Callable::ShouldBeInlined(output_type);
}
void SetUsed() { used_ = true; }
bool IsUsed() const { return used_; }
protected:
Macro(Declarable::Kind kind, std::string external_name,
std::string readable_name, const Signature& signature,
base::Optional<Statement*> body)
: Callable(kind, std::move(external_name), std::move(readable_name),
signature, body),
used_(false) {
if (signature.parameter_types.var_args) {
ReportError("Varargs are not supported for macros.");
}
}
private:
bool used_;
};
class ExternMacro : public Macro {
public:
DECLARE_DECLARABLE_BOILERPLATE(ExternMacro, ExternMacro)
const std::string& external_assembler_name() const {
return external_assembler_name_;
}
std::string CCName() const override {
return "TorqueRuntimeMacroShims::" + external_assembler_name() +
"::" + ExternalName();
}
private:
friend class Declarations;
ExternMacro(const std::string& name, std::string external_assembler_name,
Signature signature)
: Macro(Declarable::kExternMacro, name, name, std::move(signature),
base::nullopt),
external_assembler_name_(std::move(external_assembler_name)) {}
std::string external_assembler_name_;
};
class TorqueMacro : public Macro {
public:
DECLARE_DECLARABLE_BOILERPLATE(TorqueMacro, TorqueMacro)
bool IsExportedToCSA() const { return exported_to_csa_; }
std::string CCName() const override {
// Exported functions must have unique and C++-friendly readable names, so
// prefer those wherever possible.
return PrefixNameForCCOutput(IsExportedToCSA() ? ReadableName()
: ExternalName());
}
protected:
TorqueMacro(Declarable::Kind kind, std::string external_name,
std::string readable_name, const Signature& signature,
base::Optional<Statement*> body, bool is_user_defined,
bool exported_to_csa)
: Macro(kind, std::move(external_name), std::move(readable_name),
signature, body),
exported_to_csa_(exported_to_csa) {
SetIsUserDefined(is_user_defined);
}
private:
friend class Declarations;
TorqueMacro(std::string external_name, std::string readable_name,
const Signature& signature, base::Optional<Statement*> body,
bool is_user_defined, bool exported_to_csa)
: TorqueMacro(Declarable::kTorqueMacro, std::move(external_name),
std::move(readable_name), signature, body, is_user_defined,
exported_to_csa) {}
bool exported_to_csa_ = false;
};
class Method : public TorqueMacro {
public:
DECLARE_DECLARABLE_BOILERPLATE(Method, Method)
bool ShouldBeInlined(OutputType output_type) const override {
return Macro::ShouldBeInlined(output_type) ||
signature()
.parameter_types.types[signature().implicit_count]
->IsStructType();
}
AggregateType* aggregate_type() const { return aggregate_type_; }
private:
friend class Declarations;
Method(AggregateType* aggregate_type, std::string external_name,
std::string readable_name, const Signature& signature, Statement* body)
: TorqueMacro(Declarable::kMethod, std::move(external_name),
std::move(readable_name), signature, body, true, false),
aggregate_type_(aggregate_type) {}
AggregateType* aggregate_type_;
};
class Builtin : public Callable {
public:
enum Kind { kStub, kFixedArgsJavaScript, kVarArgsJavaScript };
DECLARE_DECLARABLE_BOILERPLATE(Builtin, builtin)
Kind kind() const { return kind_; }
bool IsStub() const { return kind_ == kStub; }
bool IsVarArgsJavaScript() const { return kind_ == kVarArgsJavaScript; }
bool IsFixedArgsJavaScript() const { return kind_ == kFixedArgsJavaScript; }
private:
friend class Declarations;
Builtin(std::string external_name, std::string readable_name,
Builtin::Kind kind, const Signature& signature,
base::Optional<Statement*> body)
: Callable(Declarable::kBuiltin, std::move(external_name),
std::move(readable_name), signature, body),
kind_(kind) {}
Kind kind_;
};
class RuntimeFunction : public Callable {
public:
DECLARE_DECLARABLE_BOILERPLATE(RuntimeFunction, runtime)
private:
friend class Declarations;
RuntimeFunction(const std::string& name, const Signature& signature)
: Callable(Declarable::kRuntimeFunction, name, name, signature,
base::nullopt) {}
};
class Intrinsic : public Callable {
public:
DECLARE_DECLARABLE_BOILERPLATE(Intrinsic, intrinsic)
private:
friend class Declarations;
Intrinsic(std::string name, const Signature& signature)
: Callable(Declarable::kIntrinsic, name, name, signature, base::nullopt) {
if (signature.parameter_types.var_args) {
ReportError("Varargs are not supported for intrinsics.");
}
}
};
class TypeConstraint {
public:
base::Optional<std::string> IsViolated(const Type*) const;
static TypeConstraint Unconstrained() { return {}; }
static TypeConstraint SubtypeConstraint(const Type* upper_bound) {
TypeConstraint result;
result.upper_bound = {upper_bound};
return result;
}
private:
base::Optional<const Type*> upper_bound;
};
base::Optional<std::string> FindConstraintViolation(
const std::vector<const Type*>& types,
const std::vector<TypeConstraint>& constraints);
std::vector<TypeConstraint> ComputeConstraints(
Scope* scope, const GenericParameters& parameters);
template <class SpecializationType, class DeclarationType>
class GenericDeclarable : public Declarable {
private:
using Map = std::unordered_map<TypeVector, SpecializationType,
base::hash<TypeVector>>;
public:
void AddSpecialization(const TypeVector& type_arguments,
SpecializationType specialization) {
DCHECK_EQ(0, specializations_.count(type_arguments));
if (auto violation =
FindConstraintViolation(type_arguments, Constraints())) {
Error(*violation).Throw();
}
specializations_[type_arguments] = specialization;
}
base::Optional<SpecializationType> GetSpecialization(
const TypeVector& type_arguments) const {
auto it = specializations_.find(type_arguments);
if (it != specializations_.end()) return it->second;
return base::nullopt;
}
using iterator = typename Map::const_iterator;
iterator begin() const { return specializations_.begin(); }
iterator end() const { return specializations_.end(); }
const std::string& name() const { return name_; }
auto declaration() const { return generic_declaration_->declaration; }
const GenericParameters& generic_parameters() const {
return generic_declaration_->generic_parameters;
}
const std::vector<TypeConstraint>& Constraints() {
if (!constraints_)
constraints_ = {ComputeConstraints(ParentScope(), generic_parameters())};
return *constraints_;
}
protected:
GenericDeclarable(Declarable::Kind kind, const std::string& name,
DeclarationType generic_declaration)
: Declarable(kind),
name_(name),
generic_declaration_(generic_declaration) {
DCHECK(!generic_declaration->generic_parameters.empty());
}
private:
std::string name_;
DeclarationType generic_declaration_;
Map specializations_;
base::Optional<std::vector<TypeConstraint>> constraints_;
};
class GenericCallable
: public GenericDeclarable<Callable*, GenericCallableDeclaration*> {
public:
DECLARE_DECLARABLE_BOILERPLATE(GenericCallable, generic_callable)
base::Optional<Statement*> CallableBody();
TypeArgumentInference InferSpecializationTypes(
const TypeVector& explicit_specialization_types,
const std::vector<base::Optional<const Type*>>& arguments);
private:
friend class Declarations;
GenericCallable(const std::string& name,
GenericCallableDeclaration* generic_declaration)
: GenericDeclarable<Callable*, GenericCallableDeclaration*>(
Declarable::kGenericCallable, name, generic_declaration) {}
};
class GenericType
: public GenericDeclarable<const Type*, GenericTypeDeclaration*> {
public:
DECLARE_DECLARABLE_BOILERPLATE(GenericType, generic_type)
private:
friend class Declarations;
GenericType(const std::string& name,
GenericTypeDeclaration* generic_declaration)
: GenericDeclarable<const Type*, GenericTypeDeclaration*>(
Declarable::kGenericType, name, generic_declaration) {}
};
class TypeAlias : public Declarable {
public:
DECLARE_DECLARABLE_BOILERPLATE(TypeAlias, type_alias)
const Type* type() const {
if (type_) return *type_;
return Resolve();
}
const Type* Resolve() const;
bool IsRedeclaration() const { return redeclaration_; }
SourcePosition GetDeclarationPosition() const {
return declaration_position_;
}
private:
friend class Declarations;
friend class TypeVisitor;
explicit TypeAlias(
const Type* type, bool redeclaration,
SourcePosition declaration_position = SourcePosition::Invalid())
: Declarable(Declarable::kTypeAlias),
type_(type),
redeclaration_(redeclaration),
declaration_position_(declaration_position) {}
explicit TypeAlias(
TypeDeclaration* type, bool redeclaration,
SourcePosition declaration_position = SourcePosition::Invalid())
: Declarable(Declarable::kTypeAlias),
delayed_(type),
redeclaration_(redeclaration),
declaration_position_(declaration_position) {}
mutable bool being_resolved_ = false;
mutable base::Optional<TypeDeclaration*> delayed_;
mutable base::Optional<const Type*> type_;
bool redeclaration_;
const SourcePosition declaration_position_;
};
std::ostream& operator<<(std::ostream& os, const Callable& m);
std::ostream& operator<<(std::ostream& os, const Builtin& b);
std::ostream& operator<<(std::ostream& os, const RuntimeFunction& b);
std::ostream& operator<<(std::ostream& os, const GenericCallable& g);
#undef DECLARE_DECLARABLE_BOILERPLATE
} // namespace torque
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_DECLARABLE_H_