Commit 91ef86f9 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] add typeswitch statement

This adds a typeswitch statement

typeswitch (e)
case (x1 : Type1) {
  ...
} case (x2 : Type2) {

} ...
... case (xn : TypeN) {
  ...
}

This checks to which of the given types the result of evaluating e can
be cast, in the order in which they are listed. So if an earlier
type matches, a value of this type won't reach a later case.

The type-checks are performed by calling the cast<T>() macro.
The type of the argument passed to the cast macro is dependent on the
case and excludes all types checked earlier. For example, in

const x : Object = ...
typeswitch (x)
case (x : Smi) {
  ...
} case (x : HeapNumber) {
  ...
} case (x : HeapObject) {
  ...
}

there will be calls to cast<Smi>(Object) and
cast<HeapNumber>(HeapObject), because after the Smi check we know that
x has to be a HeapObject. With the refactored base.tq definition of
cast, this will generate efficient code and avoid repeating the Smi
check in the second case.

The type system ensures that all cases are reachable and that the type
given to the last case is safe without a runtime check (in other words,
the union of all checked types covers the type of e).

The cases can also be written as
case (Type) { ... }
, in which case the switched value is not re-bound with the checked
type.

Bug: v8:7793
Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3
Reviewed-on: https://chromium-review.googlesource.com/1156506
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarDaniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54958}
parent 224763d1
......@@ -319,42 +319,50 @@ extern operator 'is<Smi>' macro TaggedIsSmi(Object): bool;
extern operator 'isnt<Smi>' macro TaggedIsNotSmi(Object): bool;
extern macro TaggedIsPositiveSmi(Object): bool;
extern macro TaggedToJSDataView(Object): JSDataView labels CastError;
extern macro HeapObjectToJSDataView(HeapObject): JSDataView labels CastError;
extern macro TaggedToHeapObject(Object): HeapObject labels CastError;
extern macro TaggedToSmi(Object): Smi labels CastError;
extern macro TaggedToJSArray(Object): JSArray labels CastError;
extern macro TaggedToCallable(Object): Callable labels CastError;
extern macro ConvertFixedArrayBaseToFixedArray(FixedArrayBase):
extern macro HeapObjectToJSArray(HeapObject): JSArray labels CastError;
extern macro HeapObjectToCallable(HeapObject): Callable labels CastError;
extern macro HeapObjectToFixedArray(HeapObject):
FixedArray labels CastError;
extern macro ConvertFixedArrayBaseToFixedDoubleArray(FixedArrayBase):
extern macro HeapObjectToFixedDoubleArray(HeapObject):
FixedDoubleArray labels CastError;
extern macro TaggedToNumber(Object): Number labels CastError;
macro cast<A : type>(o: Object): A labels CastError;
cast<Number>(o: Object): Number labels CastError {
return TaggedToNumber(o) otherwise CastError;
macro cast_HeapObject<A : type>(o : HeapObject) : A labels CastError;
cast_HeapObject<HeapObject>(o : HeapObject) : HeapObject labels CastError { return o; }
cast_HeapObject<FixedArray>(o: HeapObject): FixedArray labels CastError {
return HeapObjectToFixedArray(o) otherwise CastError;
}
cast<HeapObject>(o: Object): HeapObject labels CastError {
return TaggedToHeapObject(o) otherwise CastError;
cast_HeapObject<FixedDoubleArray>(o: HeapObject): FixedDoubleArray labels CastError {
return HeapObjectToFixedDoubleArray(o) otherwise CastError;
}
cast<Smi>(o: Object): Smi labels CastError {
return TaggedToSmi(o) otherwise CastError;
cast_HeapObject<JSDataView>(o: HeapObject): JSDataView labels CastError {
return HeapObjectToJSDataView(o) otherwise CastError;
}
cast<JSDataView>(o: Object): JSDataView labels CastError {
return TaggedToJSDataView(o) otherwise CastError;
cast_HeapObject<Callable>(o: HeapObject): Callable labels CastError {
return HeapObjectToCallable(o) otherwise CastError;
}
cast<Callable>(o: Object): Callable labels CastError {
return TaggedToCallable(o) otherwise CastError;
cast_HeapObject<JSArray>(o: HeapObject): JSArray labels CastError {
return HeapObjectToJSArray(o) otherwise CastError;
}
cast<JSArray>(o: Object): JSArray labels CastError {
return TaggedToJSArray(o) otherwise CastError;
macro cast<A : type>(o: HeapObject): A labels CastError {
return cast_HeapObject<A>(o) otherwise CastError;
}
// cast_HeapObject allows this default-implementation to be non-recursive.
// Otherwise the generated CSA code might run into infinite recursion.
macro cast<A : type>(o: Object): A labels CastError {
return cast_HeapObject<A>(
TaggedToHeapObject(o) otherwise CastError) otherwise CastError;
}
macro cast<A : type>(o: FixedArrayBase): A labels CastError;
cast<FixedArray>(o: FixedArrayBase): FixedArray labels CastError {
return ConvertFixedArrayBaseToFixedArray(o) otherwise CastError;
cast<Smi>(o: Object): Smi labels CastError {
return TaggedToSmi(o) otherwise CastError;
}
cast<FixedDoubleArray>(o: FixedArrayBase): FixedDoubleArray labels CastError {
return ConvertFixedArrayBaseToFixedDoubleArray(o) otherwise CastError;
cast<Number>(o: Object): Number labels CastError {
return TaggedToNumber(o) otherwise CastError;
}
extern macro AllocateHeapNumberWithValue(float64): HeapNumber;
......@@ -622,7 +630,7 @@ extern operator
extern operator
'[]=' macro StoreFixedArrayElement(FixedArray, intptr, Object): void;
extern operator
'[]=' macro StoreFixedArrayElementInt(
'[]=' macro StoreFixedArrayElement(
FixedArray, constexpr int31, Object): void;
extern operator
'[]=' macro StoreFixedArrayElementSmi(FixedArray, Smi, Object): void;
......@@ -724,12 +732,14 @@ extern macro Typeof(Object): Object;
// Return true iff number is NaN.
macro NumberIsNaN(number: Number): bool {
if (TaggedIsSmi(number)) {
return false;
typeswitch(number) {
case (Smi) {
return false;
} case (hn : HeapNumber) {
let value: float64 = convert<float64>(hn);
return value != value;
}
}
let value: float64 = convert<float64>(unsafe_cast<HeapNumber>(number));
return value != value;
}
extern macro BranchIfToBooleanIsTrue(Object): never labels Taken, NotTaken;
......
......@@ -68,15 +68,6 @@ class ArrayBuiltinsAssembler : public BaseBuiltinsFromDSLAssembler {
void NullPostLoopAction();
// TODO(szuend): Remove once overload resolution is fixed in Torque.
TNode<Object> LoadFixedArrayElementInt(TNode<FixedArray> array, int index) {
return LoadFixedArrayElement(array, index);
}
void StoreFixedArrayElementInt(TNode<FixedArray> array, int index,
TNode<Object> value) {
StoreFixedArrayElement(array, index, value);
}
protected:
TNode<Context> context() { return context_; }
TNode<Object> receiver() { return receiver_; }
......
......@@ -4248,8 +4248,8 @@ void CodeStubAssembler::CopyFixedArrayElements(
Comment("] CopyFixedArrayElements");
}
TNode<FixedArray> CodeStubAssembler::ConvertFixedArrayBaseToFixedArray(
TNode<FixedArrayBase> base, Label* cast_fail) {
TNode<FixedArray> CodeStubAssembler::HeapObjectToFixedArray(
TNode<HeapObject> base, Label* cast_fail) {
Label fixed_array(this);
TNode<Map> map = LoadMap(base);
GotoIf(WordEqual(map, LoadRoot(Heap::kFixedArrayMapRootIndex)), &fixed_array);
......
......@@ -306,9 +306,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
return UncheckedCast<HeapObject>(value);
}
TNode<JSArray> TaggedToJSArray(TNode<Object> value, Label* fail) {
GotoIf(TaggedIsSmi(value), fail);
TNode<HeapObject> heap_object = CAST(value);
TNode<JSArray> HeapObjectToJSArray(TNode<HeapObject> heap_object,
Label* fail) {
GotoIfNot(IsJSArray(heap_object), fail);
return UncheckedCast<JSArray>(heap_object);
}
......@@ -321,18 +320,16 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
return UncheckedCast<JSArray>(heap_object);
}
TNode<JSDataView> TaggedToJSDataView(TNode<Object> value, Label* fail) {
GotoIf(TaggedIsSmi(value), fail);
TNode<HeapObject> heap_object = CAST(value);
TNode<JSDataView> HeapObjectToJSDataView(TNode<HeapObject> heap_object,
Label* fail) {
GotoIfNot(IsJSDataView(heap_object), fail);
return UncheckedCast<JSDataView>(heap_object);
return CAST(heap_object);
}
TNode<JSReceiver> TaggedToCallable(TNode<Object> value, Label* fail) {
GotoIf(TaggedIsSmi(value), fail);
TNode<HeapObject> result = UncheckedCast<HeapObject>(value);
GotoIfNot(IsCallable(result), fail);
return CAST(result);
TNode<JSReceiver> HeapObjectToCallable(TNode<HeapObject> heap_object,
Label* fail) {
GotoIfNot(IsCallable(heap_object), fail);
return CAST(heap_object);
}
TNode<HeapNumber> UnsafeCastNumberToHeapNumber(TNode<Number> p_n) {
......@@ -1462,11 +1459,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
SMI_PARAMETERS);
}
TNode<FixedArray> ConvertFixedArrayBaseToFixedArray(
TNode<FixedArrayBase> base, Label* cast_fail);
TNode<FixedArray> HeapObjectToFixedArray(TNode<HeapObject> base,
Label* cast_fail);
TNode<FixedDoubleArray> ConvertFixedArrayBaseToFixedDoubleArray(
TNode<FixedArrayBase> base, Label* cast_fail) {
TNode<FixedDoubleArray> HeapObjectToFixedDoubleArray(TNode<HeapObject> base,
Label* cast_fail) {
GotoIf(WordNotEqual(LoadMap(base),
LoadRoot(Heap::kFixedDoubleArrayMapRootIndex)),
cast_fail);
......
......@@ -29,7 +29,8 @@ namespace torque {
V(FieldAccessExpression) \
V(ElementAccessExpression) \
V(AssignmentExpression) \
V(IncrementDecrementExpression)
V(IncrementDecrementExpression) \
V(AssumeTypeImpossibleExpression)
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
V(BasicTypeExpression) \
......@@ -209,7 +210,7 @@ class Ast {
struct IdentifierExpression : LocationExpression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(IdentifierExpression)
IdentifierExpression(SourcePosition pos, std::string name,
std::vector<TypeExpression*> args)
std::vector<TypeExpression*> args = {})
: LocationExpression(kKind, pos),
name(std::move(name)),
generic_arguments(std::move(args)) {}
......@@ -333,11 +334,29 @@ struct IncrementDecrementExpression : Expression {
bool postfix;
};
// This expression is only used in the desugaring of typeswitch, and it allows
// to bake in the static information that certain types are impossible at a
// certain position in the control flow.
// The result type is the type of {expression} minus the provided type.
struct AssumeTypeImpossibleExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(AssumeTypeImpossibleExpression)
AssumeTypeImpossibleExpression(SourcePosition pos,
TypeExpression* excluded_type,
Expression* expression)
: Expression(kKind, pos),
excluded_type(excluded_type),
expression(expression) {}
TypeExpression* excluded_type;
Expression* expression;
};
struct ParameterList {
std::vector<std::string> names;
std::vector<TypeExpression*> types;
bool has_varargs;
std::string arguments_variable;
static ParameterList Empty() { return ParameterList{{}, {}, false, ""}; }
};
struct BasicTypeExpression : TypeExpression {
......@@ -544,8 +563,8 @@ struct TryLabelStatement : Statement {
struct BlockStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(BlockStatement)
BlockStatement(SourcePosition pos, bool deferred,
std::vector<Statement*> statements)
explicit BlockStatement(SourcePosition pos, bool deferred = false,
std::vector<Statement*> statements = {})
: Statement(kKind, pos),
deferred(deferred),
statements(std::move(statements)) {}
......
......@@ -137,6 +137,8 @@ class DeclarationVisitor : public FileVisitor {
Visit(expr->location);
}
void Visit(AssumeTypeImpossibleExpression* expr) { Visit(expr->expression); }
void Visit(TryLabelStatement* stmt);
void GenerateHeader(std::string& file_name);
......
......@@ -20,9 +20,8 @@ VisitResult ImplementationVisitor::Visit(Expression* expr) {
AST_EXPRESSION_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNIMPLEMENTED();
UNREACHABLE();
}
return VisitResult();
}
const Type* ImplementationVisitor::Visit(Statement* stmt) {
......@@ -519,6 +518,18 @@ VisitResult ImplementationVisitor::Visit(NumberLiteralExpression* expr) {
return VisitResult{result_type, temp};
}
VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) {
VisitResult result = Visit(expr->expression);
const Type* result_type =
SubtractType(result.type(), declarations()->GetType(expr->excluded_type));
if (result_type->IsNever()) {
ReportError("unreachable code");
}
return VisitResult{result_type, "UncheckedCast<" +
result_type->GetGeneratedTNodeTypeName() +
">(" + result.RValue() + ")"};
}
VisitResult ImplementationVisitor::Visit(StringLiteralExpression* expr) {
std::string temp = GenerateNewTempVariable(TypeOracle::GetConstStringType());
source_out() << "\"" << expr->literal.substr(1, expr->literal.size() - 2)
......@@ -1429,10 +1440,11 @@ VisitResult ImplementationVisitor::GenerateFetchFromLocation(
VisitResult ImplementationVisitor::GenerateFetchFromLocation(
FieldAccessExpression* expr, LocationReference reference) {
const Type* type = reference.base.type();
if (reference.value != nullptr) {
return GenerateFetchFromLocation(reference);
} else if (const StructType* struct_type = StructType::DynamicCast(type)) {
}
const Type* type = reference.base.type();
if (const StructType* struct_type = StructType::DynamicCast(type)) {
return VisitResult(struct_type->GetFieldType(expr->field),
reference.base.RValue() + "." + expr->field);
} else {
......@@ -1940,7 +1952,6 @@ VisitResult ImplementationVisitor::GenerateImplicitConvert(
<< " as a value of type " << *destination_type;
ReportError(s.str());
}
return VisitResult(TypeOracle::GetVoidType(), "");
}
std::string ImplementationVisitor::NewTempVariable() {
......
......@@ -119,6 +119,7 @@ class ImplementationVisitor : public FileVisitor {
VisitResult Visit(AssignmentExpression* expr);
VisitResult Visit(StringLiteralExpression* expr);
VisitResult Visit(NumberLiteralExpression* expr);
VisitResult Visit(AssumeTypeImpossibleExpression* expr);
const Type* Visit(TryLabelStatement* stmt);
const Type* Visit(ReturnStatement* stmt);
......
......@@ -22,6 +22,13 @@ struct ExpressionWithSource {
std::string source;
};
struct TypeswitchCase {
SourcePosition pos;
base::Optional<std::string> name;
TypeExpression* type;
Statement* block;
};
enum class ParseResultHolderBase::TypeId {
kStdString,
kBool,
......@@ -49,7 +56,9 @@ enum class ParseResultHolderBase::TypeId {
kStdVectorOfLabelAndTypes,
kStdVectorOfLabelBlockPtr,
kOptionalStatementPtr,
kOptionalExpressionPtr
kOptionalExpressionPtr,
kTypeswitchCase,
kStdVectorOfTypeswitchCase
};
template <>
......@@ -151,6 +160,13 @@ template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<base::Optional<Expression*>>::id =
ParseResultTypeId::kOptionalExpressionPtr;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<TypeswitchCase>::id = ParseResultTypeId::kTypeswitchCase;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<TypeswitchCase>>::id =
ParseResultTypeId::kStdVectorOfTypeswitchCase;
namespace {
......@@ -468,6 +484,93 @@ base::Optional<ParseResult> MakeIfStatement(
return ParseResult{result};
}
base::Optional<ParseResult> MakeTypeswitchStatement(
ParseResultIterator* child_results) {
auto expression = child_results->NextAs<Expression*>();
auto cases = child_results->NextAs<std::vector<TypeswitchCase>>();
CurrentSourcePosition::Scope current_source_position(
child_results->matched_input().pos);
// typeswitch (expression) case (x1 : T1) {
// ...b1
// } case (x2 : T2) {
// ...b2
// } case (x3 : T3) {
// ...b3
// }
//
// desugars to
//
// {
// const _value = expression;
// try {
// const x1 : T1 = cast<T1>(_value) otherwise _NextCase;
// ...b1
// } label _NextCase {
// try {
// const x2 : T2 = cast<T2>(%assume_impossible<T1>(_value));
// ...b2
// } label _NextCase {
// const x3 : T3 = %assume_impossible<T1|T2>(_value);
// ...b3
// }
// }
// }
BlockStatement* current_block = MakeNode<BlockStatement>();
Statement* result = current_block;
{
CurrentSourcePosition::Scope current_source_position(expression->pos);
current_block->statements.push_back(MakeNode<VarDeclarationStatement>(
true, "_value", base::nullopt, expression));
}
TypeExpression* accumulated_types;
for (size_t i = 0; i < cases.size(); ++i) {
CurrentSourcePosition::Scope current_source_position(cases[i].pos);
Expression* value = MakeNode<IdentifierExpression>("_value");
if (i >= 1) {
value =
MakeNode<AssumeTypeImpossibleExpression>(accumulated_types, value);
}
BlockStatement* case_block;
if (i < cases.size() - 1) {
value = MakeNode<CallExpression>(
"cast", false, std::vector<TypeExpression*>{cases[i].type},
std::vector<Expression*>{value},
std::vector<std::string>{"_NextCase"});
case_block = MakeNode<BlockStatement>();
} else {
case_block = current_block;
}
std::string name = "_case_value";
if (cases[i].name) name = *cases[i].name;
case_block->statements.push_back(
MakeNode<VarDeclarationStatement>(true, name, cases[i].type, value));
case_block->statements.push_back(cases[i].block);
if (i < cases.size() - 1) {
BlockStatement* next_block = MakeNode<BlockStatement>();
current_block->statements.push_back(MakeNode<TryLabelStatement>(
case_block, std::vector<LabelBlock*>{MakeNode<LabelBlock>(
"_NextCase", ParameterList::Empty(), next_block)}));
current_block = next_block;
}
accumulated_types =
i > 0 ? MakeNode<UnionTypeExpression>(accumulated_types, cases[i].type)
: cases[i].type;
}
return ParseResult{result};
}
base::Optional<ParseResult> MakeTypeswitchCase(
ParseResultIterator* child_results) {
auto name = child_results->NextAs<base::Optional<std::string>>();
auto type = child_results->NextAs<TypeExpression*>();
auto block = child_results->NextAs<Statement*>();
return ParseResult{TypeswitchCase{child_results->matched_input().pos,
std::move(name), type, block}};
}
base::Optional<ParseResult> MakeWhileStatement(
ParseResultIterator* child_results) {
auto condition = child_results->NextAs<Expression*>();
......@@ -1068,6 +1171,13 @@ struct TorqueGrammar : Grammar {
Token(")"), &atomarStatement,
Optional<Statement*>(Sequence({Token("else"), &statement}))},
MakeIfStatement),
Rule(
{
Token("typeswitch"), Token("("), expression, Token(")"),
Token("{"), NonemptyList<TypeswitchCase>(&typeswitchCase),
Token("}"),
},
MakeTypeswitchStatement),
Rule({Token("try"), &block, NonemptyList<LabelBlock*>(&labelBlock)},
MakeTryLabelStatement),
Rule({OneOf({"assert", "check"}), Token("("), &expressionWithSource,
......@@ -1086,6 +1196,13 @@ struct TorqueGrammar : Grammar {
Optional<Expression*>(expression), Token(")"), &atomarStatement},
MakeForLoopStatement)};
// Result: TypeswitchCase
Symbol typeswitchCase = {
Rule({Token("case"), Token("("),
Optional<std::string>(Sequence({&identifier, Token(":")})), &type,
Token(")"), &block},
MakeTypeswitchCase)};
// Result: base::Optional<Statement*>
Symbol optionalBody = {
Rule({&block}, CastParseResult<Statement*, base::Optional<Statement*>>),
......
......@@ -32,6 +32,7 @@ std::string Type::ToString() const {
}
bool Type::IsSubtypeOf(const Type* supertype) const {
if (IsNever()) return true;
if (const UnionType* union_type = UnionType::DynamicCast(supertype)) {
return union_type->IsSupertypeOf(this);
}
......@@ -153,6 +154,36 @@ const Type* UnionType::NonConstexprVersion() const {
return this;
}
void UnionType::RecomputeParent() {
const Type* parent = nullptr;
for (const Type* t : types_) {
if (parent == nullptr) {
parent = t;
} else {
parent = CommonSupertype(parent, t);
}
}
set_parent(parent);
}
void UnionType::Subtract(const Type* t) {
for (auto it = types_.begin(); it != types_.end();) {
if ((*it)->IsSubtypeOf(t)) {
it = types_.erase(it);
} else {
++it;
}
}
if (types_.size() == 0) types_.insert(TypeOracle::GetNeverType());
RecomputeParent();
}
const Type* SubtractType(const Type* a, const Type* b) {
UnionType result = UnionType::FromType(a);
result.Subtract(b);
return TypeOracle::GetUnionType(result);
}
std::string StructType::ToExplicitString() const {
std::stringstream result;
result << "{";
......
......@@ -248,13 +248,6 @@ class UnionType final : public Type {
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;
......@@ -288,6 +281,8 @@ class UnionType final : public Type {
}
}
void Subtract(const Type* t);
static UnionType FromType(const Type* t) {
const UnionType* union_type = UnionType::DynamicCast(t);
return union_type ? UnionType(*union_type) : UnionType(t);
......@@ -295,10 +290,13 @@ class UnionType final : public Type {
private:
explicit UnionType(const Type* t) : Type(Kind::kUnionType, t), types_({t}) {}
void RecomputeParent();
std::set<const Type*, TypeLess> types_;
};
const Type* SubtractType(const Type* a, const Type* b);
class StructType final : public Type {
public:
DECLARE_TYPE_BOILERPLATE(StructType);
......@@ -351,14 +349,13 @@ class VisitResult {
: type_(type), value_(value), declarable_{} {}
VisitResult(const Type* type, const Value* declarable);
const Type* type() const { return type_; }
// const std::string& variable() const { return variable_; }
base::Optional<const Value*> declarable() const { return declarable_; }
std::string LValue() const;
std::string RValue() const;
void SetType(const Type* new_type) { type_ = new_type; }
private:
const Type* type_;
const Type* type_ = nullptr;
std::string value_;
base::Optional<const Value*> declarable_;
};
......
......@@ -235,6 +235,18 @@ TEST(TestForLoop) {
ft.Call();
}
TEST(TestTypeswitch) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeAssemblerTester asm_tester(isolate, 0);
TestBuiltinsFromDSLAssembler m(asm_tester.state());
{
m.TestTypeswitch();
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
TEST(TestGenericOverload) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeAssemblerTester asm_tester(isolate, 0);
......
......@@ -387,6 +387,53 @@ module test {
check(sum == 7);
}
macro TestSubtyping(x : Smi) {
const foo : Object = x;
}
macro IncrementIfSmi<A : type>(x : A) : A {
typeswitch (x) {
case (x : Smi) {
return x + 1;
} case (o : A) {
return o;
}
}
}
macro TypeswitchExample(x : Number | FixedArray) : int32 {
let result : int32 = 0;
typeswitch (IncrementIfSmi<(Number|FixedArray)>(x)) {
case (x : FixedArray) {
result = result + 1;
} case (Number) {
result = result + 2;
}
}
result = result * 10;
typeswitch (IncrementIfSmi<(Number|FixedArray)>(x)) {
case (x : Smi) {
result = result + convert<int32>(x);
} case (a : FixedArray) {
result = result + convert<int32>(a.length);
} case (x : HeapNumber) {
result = result + 7;
}
}
return result;
}
macro TestTypeswitch() {
check(TypeswitchExample(from_constexpr<Smi>(5)) == 26);
const a : FixedArray = AllocateFixedArray(PACKED_ELEMENTS, 3);
a[0] = a[1] = a[2] = from_constexpr<Smi>(0);
check(TypeswitchExample(a) == 13);
check(TypeswitchExample(from_constexpr<Number>(0.5)) == 27);
}
macro ExampleGenericOverload<A: type>(o : Object) : A {
return o;
}
......
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