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

[torque] add union types

This adds support for union types to Torque.

There is a new type expression
A | B
to form the union of the type expressions A and B.
This is only possible if A and B have a common supertype, to prevent
nonsensical unions of types with different representations.

Union types are normalized:
A | B == B | A
A | (B | C) == (A | B) | C
A | A == A

The subtyping rules are defined recursively:
(A | B) <: C  if  A <: C and B <: C
A <: (B | C)  if  A <: B or A <: C

This allows to define Object as a union type:

type Tagged generates 'TNode<Object>';
type Smi extends Tagged generates 'TNode<Smi>';
type HeapObject extends Tagged generates 'TNode<HeapObject>';
type Object = Smi | HeapObject;

The type {Tagged} is introduced to have a common supertype of all
tagged values, but we should not use it directly, because {Object}
contains the additional information that there is nothing but {Smi}
and {HeapObject} values.

When mapping union types to CSA types, we select the most specific
common supertype. For Number and Numeric, we already use union types
on the CSA side. Since it is not possible to map to CSA union types
in general, we special-case these two union types to map them to
the CSA union types we already use.

Bug: v8:7793
Change-Id: I7a4e466436f55d04012f29ef17acfdb957653908
Reviewed-on: https://chromium-review.googlesource.com/1076132Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53411}
parent 57cc9eae
......@@ -6,7 +6,10 @@ type Arguments constexpr 'CodeStubArguments*';
type void generates 'void';
type never generates 'void';
type Object generates 'TNode<Object>';
type Tagged generates 'TNode<Object>';
type Smi extends Tagged generates 'TNode<Smi>';
type HeapObject extends Tagged generates 'TNode<HeapObject>';
type Object = Smi | HeapObject;
type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
type float64 generates 'TNode<Float64T>' constexpr 'double';
......@@ -14,9 +17,6 @@ type bool generates 'TNode<BoolT>' constexpr 'bool';
type int31 extends int32 generates 'TNode<Int32T>' constexpr 'int32_t';
type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
type Number extends Object generates 'TNode<Number>';
type Smi extends Number generates 'TNode<Smi>';
type HeapObject extends Object generates 'TNode<HeapObject>';
type AbstractCode extends HeapObject generates 'TNode<AbstractCode>';
type Code extends AbstractCode generates 'TNode<Code>';
type JSReceiver extends HeapObject generates 'TNode<JSReceiver>';
......@@ -24,11 +24,14 @@ type Context extends HeapObject generates 'TNode<Context>';
type String extends HeapObject generates 'TNode<String>';
type Oddball extends HeapObject generates 'TNode<Oddball>';
type HeapNumber extends HeapObject generates 'TNode<HeapNumber>';
type Number = Smi | HeapNumber;
type Boolean extends Oddball generates 'TNode<Oddball>';
type JSProxy extends JSReceiver generates 'TNode<JSProxy>';
type JSObject extends JSReceiver generates 'TNode<JSObject>';
type JSArray extends JSObject generates 'TNode<JSArray>';
type Callable extends JSReceiver generates 'TNode<JSReceiver>';
type JSFunction extends Callable generates 'TNode<JSFunction>';
type JSFunction extends JSObject generates 'TNode<JSFunction>';
type JSBoundFunction extends JSObject generates 'TNode<JSBoundFunction>';
type Callable = JSFunction | JSBoundFunction | JSProxy;
type Map extends HeapObject generates 'TNode<Map>';
type FixedArrayBase extends HeapObject generates 'TNode<FixedArrayBase>';
type FixedArray extends FixedArrayBase generates 'TNode<FixedArray>';
......
......@@ -120,7 +120,10 @@ DECIMAL_LITERAL
type : CONSTEXPR? IDENTIFIER
| BUILTIN '(' typeList ')' '=>' type
| type BIT_OR type
| '(' type ')'
;
typeList : (type (',' type)*)?;
genericSpecializationTypeList: '<' typeList '>';
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -262,7 +262,9 @@ class TorqueParser : public antlr4::Parser {
antlr4::tree::TerminalNode* CONSTEXPR();
antlr4::tree::TerminalNode* BUILTIN();
TypeListContext* typeList();
TypeContext* type();
std::vector<TypeContext*> type();
TypeContext* type(size_t i);
antlr4::tree::TerminalNode* BIT_OR();
void enterRule(antlr4::tree::ParseTreeListener* listener) override;
void exitRule(antlr4::tree::ParseTreeListener* listener) override;
......@@ -271,7 +273,7 @@ class TorqueParser : public antlr4::Parser {
};
TypeContext* type();
TypeContext* type(int precedence);
class TypeListContext : public antlr4::ParserRuleContext {
public:
TypeListContext(antlr4::ParserRuleContext* parent, size_t invokingState);
......@@ -1481,6 +1483,7 @@ class TorqueParser : public antlr4::Parser {
bool sempred(antlr4::RuleContext* _localctx, size_t ruleIndex,
size_t predicateIndex) override;
bool typeSempred(TypeContext* _localctx, size_t predicateIndex);
bool conditionalExpressionSempred(ConditionalExpressionContext* _localctx,
size_t predicateIndex);
bool logicalORExpressionSempred(LogicalORExpressionContext* _localctx,
......
......@@ -76,14 +76,20 @@ LabelAndTypesVector AstGenerator::GetOptionalLabelAndTypeList(
TypeExpression* AstGenerator::GetType(TorqueParser::TypeContext* context) {
if (context->BUILTIN()) {
ParameterList parameters = context->typeList()->accept(this);
TypeExpression* return_type = GetType(context->type());
TypeExpression* return_type = GetType(context->type(0));
return RegisterNode(
new FunctionTypeExpression(Pos(context), parameters, return_type));
} else {
} else if (context->BIT_OR()) {
return RegisterNode(new UnionTypeExpression(
Pos(context), GetType(context->type(0)), GetType(context->type(1))));
} else if (context->IDENTIFIER()) {
bool is_constexpr = context->CONSTEXPR() != nullptr;
std::string name = context->IDENTIFIER()->getSymbol()->getText();
return RegisterNode(
new BasicTypeExpression(Pos(context), is_constexpr, std::move(name)));
} else {
DCHECK_EQ(1, context->type().size());
return GetType(context->type(0));
}
}
......
......@@ -44,7 +44,8 @@ DECLARE_CONTEXTUAL_VARIABLE(CurrentSourcePosition, SourcePosition)
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
V(BasicTypeExpression) \
V(FunctionTypeExpression)
V(FunctionTypeExpression) \
V(UnionTypeExpression)
#define AST_STATEMENT_NODE_KIND_LIST(V) \
V(BlockStatement) \
......@@ -380,6 +381,14 @@ struct FunctionTypeExpression : TypeExpression {
TypeExpression* return_type;
};
struct UnionTypeExpression : TypeExpression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(UnionTypeExpression)
UnionTypeExpression(SourcePosition pos, TypeExpression* a, TypeExpression* b)
: TypeExpression(kKind, pos), a(a), b(b) {}
TypeExpression* a;
TypeExpression* b;
};
struct ExpressionStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ExpressionStatement)
ExpressionStatement(SourcePosition p, Expression* e)
......
......@@ -85,8 +85,9 @@ class DeclarationVisitor : public FileVisitor {
}
void Visit(TypeAliasDeclaration* decl) {
declarations()->DeclareType(decl->name,
declarations()->GetType(decl->type));
const Type* type = declarations()->GetType(decl->type);
type->AddAlias(decl->name);
declarations()->DeclareType(decl->name, type);
}
Builtin* BuiltinDeclarationCommon(BuiltinDeclaration* decl, bool external,
......
......@@ -72,18 +72,31 @@ const AbstractType* Declarations::GetAbstractType(const Type* parent,
return result;
}
const Type* Declarations::GetFunctionPointerType(TypeVector argument_types,
const Type* return_type) {
const FunctionPointerType* Declarations::GetFunctionPointerType(
TypeVector argument_types, const Type* return_type) {
const Type* code_type = LookupGlobalType(CODE_TYPE_STRING);
return function_pointer_types_.Add(
FunctionPointerType(code_type, argument_types, return_type));
}
const Type* Declarations::GetUnionType(const Type* a, const Type* b) {
if (a->IsSubtypeOf(b)) return b;
if (b->IsSubtypeOf(a)) return a;
UnionType result = UnionType::FromType(a);
result.Extend(b);
if (base::Optional<const Type*> single = result.GetSingleMember()) {
return *single;
}
return union_types_.Add(std::move(result));
}
const Type* Declarations::GetType(TypeExpression* type_expression) {
if (auto* basic = BasicTypeExpression::DynamicCast(type_expression)) {
std::string name =
(basic->is_constexpr ? CONSTEXPR_TYPE_PREFIX : "") + basic->name;
return LookupType(name);
} else if (auto* union_type = UnionTypeExpression::cast(type_expression)) {
return GetUnionType(GetType(union_type->a), GetType(union_type->b));
} else {
auto* function_type_exp = FunctionTypeExpression::cast(type_expression);
TypeVector argument_types;
......
......@@ -49,8 +49,10 @@ class Declarations {
const AbstractType* GetAbstractType(const Type* parent, std::string name,
std::string generated);
const Type* GetFunctionPointerType(TypeVector argument_types,
const Type* return_type);
const FunctionPointerType* GetFunctionPointerType(TypeVector argument_types,
const Type* return_type);
const Type* GetUnionType(const Type* a, const Type* b);
Builtin* FindSomeInternalBuiltinWithType(const FunctionPointerType* type);
......@@ -136,6 +138,7 @@ class Declarations {
Statement* next_body_;
std::vector<std::unique_ptr<Declarable>> declarables_;
Deduplicator<FunctionPointerType> function_pointer_types_;
Deduplicator<UnionType> union_types_;
std::vector<std::unique_ptr<Type>> nominal_types_;
std::map<std::pair<const AstNode*, TypeVector>, Scope*> scopes_;
std::map<Generic*, ScopeChain::Snapshot> generic_declaration_scopes_;
......
......@@ -12,7 +12,29 @@ namespace v8 {
namespace internal {
namespace torque {
std::string Type::ToString() const {
if (aliases_.size() == 0) return ToExplicitString();
if (aliases_.size() == 1) return *aliases_.begin();
std::stringstream result;
int i = 0;
for (const std::string& alias : aliases_) {
if (i == 0) {
result << alias << " (aka. ";
} else if (i == 1) {
result << alias;
} else {
result << ", " << alias;
}
++i;
}
result << ")";
return result.str();
}
bool Type::IsSubtypeOf(const Type* supertype) const {
if (const UnionType* union_type = UnionType::DynamicCast(supertype)) {
return union_type->IsSupertypeOf(this);
}
const Type* subtype = this;
while (subtype != nullptr) {
if (subtype == supertype) return true;
......@@ -21,6 +43,30 @@ bool Type::IsSubtypeOf(const Type* supertype) const {
return false;
}
// static
const Type* Type::CommonSupertype(const Type* a, const Type* b) {
int diff = a->Depth() - b->Depth();
const Type* a_supertype = a;
const Type* b_supertype = b;
for (; diff > 0; --diff) a_supertype = a_supertype->parent();
for (; diff < 0; ++diff) b_supertype = b_supertype->parent();
while (a_supertype && b_supertype) {
if (a_supertype == b_supertype) return a_supertype;
a_supertype = a_supertype->parent();
b_supertype = b_supertype->parent();
}
ReportError("types " + a->ToString() + " and " + b->ToString() +
" have no common supertype");
}
int Type::Depth() const {
int result = 0;
for (const Type* current = parent_; current; current = current->parent_) {
++result;
}
return result;
}
bool Type::IsAbstractName(const std::string& name) const {
if (!IsAbstractType()) return false;
return AbstractType::cast(this)->name() == name;
......@@ -33,7 +79,7 @@ std::string AbstractType::GetGeneratedTNodeTypeName() const {
return result;
}
std::string FunctionPointerType::ToString() const {
std::string FunctionPointerType::ToExplicitString() const {
std::stringstream result;
result << "builtin (";
bool first = true;
......@@ -51,12 +97,7 @@ std::string FunctionPointerType::ToString() const {
std::string FunctionPointerType::MangledName() const {
std::stringstream result;
result << "FT";
bool first = true;
for (const Type* t : parameter_types_) {
if (!first) {
result << ", ";
first = false;
}
std::string arg_type_string = t->MangledName();
result << arg_type_string.size() << arg_type_string;
}
......@@ -65,6 +106,47 @@ std::string FunctionPointerType::MangledName() const {
return result.str();
}
std::string UnionType::ToExplicitString() const {
std::stringstream result;
result << "(";
bool first = true;
for (const Type* t : types_) {
if (!first) {
result << " | ";
}
first = false;
result << t;
}
result << ")";
return result.str();
}
std::string UnionType::MangledName() const {
std::stringstream result;
result << "UT";
for (const Type* t : types_) {
std::string arg_type_string = t->MangledName();
result << arg_type_string.size() << arg_type_string;
}
return result.str();
}
std::string UnionType::GetGeneratedTNodeTypeName() const {
if (types_.size() <= 3) {
std::set<std::string> members;
for (const Type* t : types_) {
members.insert(t->GetGeneratedTNodeTypeName());
}
if (members == std::set<std::string>{"Smi", "HeapNumber"}) {
return "Number";
}
if (members == std::set<std::string>{"Smi", "HeapNumber", "BigInt"}) {
return "Numeric";
}
}
return parent()->GetGeneratedTNodeTypeName();
}
std::ostream& operator<<(std::ostream& os, const Signature& sig) {
os << "(";
for (size_t i = 0; i < sig.parameter_names.size(); ++i) {
......
......@@ -5,9 +5,11 @@
#ifndef V8_TORQUE_TYPES_H_
#define V8_TORQUE_TYPES_H_
#include <set>
#include <string>
#include <vector>
#include "src/base/optional.h"
#include "src/torque/utils.h"
namespace v8 {
......@@ -33,12 +35,13 @@ class Label;
class TypeBase {
public:
enum class Kind { kAbstractType, kFunctionPointerType };
enum class Kind { kAbstractType, kFunctionPointerType, kUnionType };
virtual ~TypeBase() {}
bool IsAbstractType() const { return kind() == Kind::kAbstractType; }
bool IsFunctionPointerType() const {
return kind() == Kind::kFunctionPointerType;
}
bool IsUnionType() const { return kind() == Kind::kUnionType; }
protected:
explicit TypeBase(Kind kind) : kind_(kind) {}
......@@ -70,8 +73,9 @@ class TypeBase {
class Type : public TypeBase {
public:
bool IsSubtypeOf(const Type* supertype) const;
virtual std::string ToString() const = 0;
virtual bool IsSubtypeOf(const Type* supertype) const;
std::string ToString() const;
virtual std::string MangledName() const = 0;
bool IsVoid() const { return IsAbstractName(VOID_TYPE_STRING); }
bool IsNever() const { return IsAbstractName(NEVER_TYPE_STRING); }
......@@ -80,20 +84,26 @@ class Type : public TypeBase {
return IsAbstractName(CONSTEXPR_BOOL_TYPE_STRING);
}
bool IsVoidOrNever() const { return IsVoid() || IsNever(); }
virtual const std::string& GetGeneratedTypeName() const = 0;
virtual std::string GetGeneratedTypeName() const = 0;
virtual std::string GetGeneratedTNodeTypeName() const = 0;
virtual bool IsConstexpr() const = 0;
static const Type* CommonSupertype(const Type* a, const Type* b);
void AddAlias(std::string alias) const { aliases_.insert(std::move(alias)); }
protected:
Type(TypeBase::Kind kind, const Type* parent)
: TypeBase(kind), parent_(parent) {}
const Type* parent() const { return parent_; }
void set_parent(const Type* t) { parent_ = t; }
int Depth() const;
virtual std::string ToExplicitString() const = 0;
private:
bool IsAbstractName(const std::string& name) const;
// If {parent_} is not nullptr, then this type is a subtype of {parent_}.
const Type* const parent_;
const Type* parent_;
mutable std::set<std::string> aliases_;
};
using TypeVector = std::vector<const Type*>;
......@@ -102,11 +112,9 @@ class AbstractType final : public Type {
public:
DECLARE_TYPE_BOILERPLATE(AbstractType);
const std::string& name() const { return name_; }
std::string ToString() const override { return name(); }
std::string ToExplicitString() const override { return name(); }
std::string MangledName() const override { return "AT" + name(); }
const std::string& GetGeneratedTypeName() const override {
return generated_type_;
}
std::string GetGeneratedTypeName() const override { return generated_type_; }
std::string GetGeneratedTNodeTypeName() const override;
bool IsConstexpr() const override {
return name().substr(0, strlen(CONSTEXPR_TYPE_PREFIX)) ==
......@@ -130,9 +138,9 @@ class AbstractType final : public Type {
class FunctionPointerType final : public Type {
public:
DECLARE_TYPE_BOILERPLATE(FunctionPointerType);
std::string ToString() const override;
std::string ToExplicitString() const override;
std::string MangledName() const override;
const std::string& GetGeneratedTypeName() const override {
std::string GetGeneratedTypeName() const override {
return parent()->GetGeneratedTypeName();
}
std::string GetGeneratedTNodeTypeName() const override {
......@@ -167,6 +175,91 @@ class FunctionPointerType final : public Type {
const Type* const return_type_;
};
class UnionType final : public Type {
public:
DECLARE_TYPE_BOILERPLATE(UnionType);
std::string ToExplicitString() const override;
std::string MangledName() const override;
std::string GetGeneratedTypeName() const override {
return "TNode<" + GetGeneratedTNodeTypeName() + ">";
}
std::string GetGeneratedTNodeTypeName() const override;
bool IsConstexpr() const override {
DCHECK_EQ(false, parent()->IsConstexpr());
return false;
}
friend size_t hash_value(const UnionType& p) {
size_t result = 0;
for (const Type* t : p.types_) {
result = base::hash_combine(result, t);
}
return result;
}
bool operator==(const UnionType& other) const {
return types_ == other.types_;
}
base::Optional<const Type*> GetSingleMember() const {
if (types_.size() == 1) {
DCHECK_EQ(*types_.begin(), parent());
return *types_.begin();
}
return base::nullopt;
}
const Type* Normalize() const {
if (types_.size() == 1) {
return parent();
}
return this;
}
bool IsSubtypeOf(const Type* other) const override {
for (const Type* member : types_) {
if (!member->IsSubtypeOf(other)) return false;
}
return true;
}
bool IsSupertypeOf(const Type* other) const {
for (const Type* member : types_) {
if (other->IsSubtypeOf(member)) {
return true;
}
}
return false;
}
void Extend(const Type* t) {
if (const UnionType* union_type = UnionType::DynamicCast(t)) {
for (const Type* member : union_type->types_) {
Extend(member);
}
} else {
if (t->IsSubtypeOf(this)) return;
set_parent(CommonSupertype(parent(), t));
for (const Type* member : types_) {
if (member->IsSubtypeOf(t)) {
types_.erase(member);
}
}
types_.insert(t);
}
}
static UnionType FromType(const Type* t) {
const UnionType* union_type = UnionType::DynamicCast(t);
return union_type ? UnionType(*union_type) : UnionType(t);
}
private:
explicit UnionType(const Type* t) : Type(Kind::kUnionType, t), types_({t}) {}
std::set<const Type*> types_;
};
inline std::ostream& operator<<(std::ostream& os, const Type* t) {
os << t->ToString();
return os;
......
......@@ -18,7 +18,7 @@ std::string CurrentPositionAsString() {
return PositionAsString(CurrentSourcePosition::Get());
}
void ReportError(const std::string& error) {
[[noreturn]] void ReportError(const std::string& error) {
std::cerr << CurrentPositionAsString() << ": Torque error: " << error << "\n";
throw(-1);
}
......
......@@ -17,7 +17,7 @@ namespace torque {
typedef std::vector<std::string> NameVector;
void ReportError(const std::string& error);
[[noreturn]] void ReportError(const std::string& error);
std::string CamelifyString(const std::string& underscore_string);
std::string DashifyString(const std::string& underscore_string);
......
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