Commit 8c17e114 authored by Daniel Clifford's avatar Daniel Clifford Committed by Commit Bot

Revert "[torque] Implement safe initialization of classes through hidden structs"

This reverts commit d11a0648.

Reason for revert: <INSERT REASONING HERE>

Original change's description:
> [torque] Implement safe initialization of classes through hidden structs
> 
> Initialization of classes now happens atomically at the end of the
> class constructor only once all of the values for the class' fields
> have been fully computed. This makes Torque constructors completely
> GC safe, e.g. hardened against allocations or exceptions in
> constructors.
> 
> As part of this change, make the 'this' parameter for method calls
> explicit rather than implicit.
> 
> Drive by: add validation to check for duplicate field declarations
> 
> Bug: v8:7793
> Change-Id: I8b5e85980d6a103ef9fc3262b76f6514f36ebf88
> Reviewed-on: https://chromium-review.googlesource.com/c/1411252
> Commit-Queue: Daniel Clifford <danno@chromium.org>
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#58979}

TBR=danno@chromium.org,tebbi@chromium.org

Change-Id: Id6c46c175f53c5a77db1e6ca242586fba34cd02e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:7793
Reviewed-on: https://chromium-review.googlesource.com/c/1426121Reviewed-by: 's avatarDaniel Clifford <danno@chromium.org>
Commit-Queue: Daniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58980}
parent d11a0648
......@@ -688,10 +688,6 @@ struct NameAndTypeExpression {
TypeExpression* type;
};
struct StructFieldExpression {
NameAndTypeExpression name_and_type;
};
struct ClassFieldExpression {
NameAndTypeExpression name_and_type;
bool weak;
......@@ -887,32 +883,33 @@ struct StructDeclaration : Declaration {
DEFINE_AST_NODE_LEAF_BOILERPLATE(StructDeclaration)
StructDeclaration(SourcePosition pos, std::string name,
std::vector<Declaration*> methods,
std::vector<StructFieldExpression> fields)
std::vector<NameAndTypeExpression> fields)
: Declaration(kKind, pos),
name(std::move(name)),
methods(std::move(methods)),
fields(std::move(fields)) {}
std::string name;
std::vector<Declaration*> methods;
std::vector<StructFieldExpression> fields;
std::vector<NameAndTypeExpression> fields;
};
struct ClassDeclaration : Declaration {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ClassDeclaration)
ClassDeclaration(SourcePosition pos, std::string name, bool transient,
std::string super, base::Optional<std::string> generates,
base::Optional<std::string> extends,
base::Optional<std::string> generates,
std::vector<Declaration*> methods,
std::vector<ClassFieldExpression> fields)
: Declaration(kKind, pos),
name(std::move(name)),
transient(transient),
super(std::move(super)),
extends(std::move(extends)),
generates(std::move(generates)),
methods(std::move(methods)),
fields(std::move(fields)) {}
std::string name;
bool transient;
std::string super;
base::Optional<std::string> extends;
base::Optional<std::string> generates;
std::vector<Declaration*> methods;
std::vector<ClassFieldExpression> fields;
......
......@@ -326,9 +326,7 @@ class Method : public Macro {
DECLARE_DECLARABLE_BOILERPLATE(Method, Method);
bool ShouldBeInlined() const override {
return Macro::ShouldBeInlined() ||
signature()
.parameter_types.types[signature().implicit_count]
->IsStructType();
signature().parameter_types.types[0]->IsStructType();
}
AggregateType* aggregate_type() const { return aggregate_type_; }
......
......@@ -163,6 +163,13 @@ void DeclarationVisitor::Visit(GenericDeclaration* decl) {
Declarations::DeclareGeneric(decl->callable->name, decl);
}
static Statement* WrapBodyWithNoThrow(Statement* body, std::string reason) {
return MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
true, MakeNode<StatementExpression>(body),
MakeNode<LabelBlock>("_catch", ParameterList{},
MakeNode<DebugStatement>(reason, true))));
}
void DeclarationVisitor::Visit(SpecializationDeclaration* decl) {
if ((decl->body != nullptr) == decl->external) {
std::stringstream stream;
......@@ -234,27 +241,21 @@ void DeclarationVisitor::DeclareMethods(
// Declare the class' methods
IdentifierExpression* constructor_this = MakeNode<IdentifierExpression>(
std::vector<std::string>{}, kThisParameterName);
AggregateType* constructor_this_type =
container_type->IsStructType()
? container_type
: ClassType::cast(container_type)->struct_type();
for (auto declaration : methods) {
CurrentSourcePosition::Scope pos_scope(declaration->pos);
StandardDeclaration* standard_declaration =
StandardDeclaration::DynamicCast(declaration);
DCHECK(standard_declaration);
TorqueMacroDeclaration* method =
TorqueMacroDeclaration::DynamicCast(standard_declaration->callable);
Signature signature = MakeSignature(method->signature.get());
signature.parameter_names.insert(
signature.parameter_names.begin() + signature.implicit_count,
signature.parameter_names.insert(signature.parameter_names.begin(),
kThisParameterName);
signature.parameter_types.types.insert(
signature.parameter_types.types.begin(), container_type);
signature.implicit_count++;
Statement* body = *(standard_declaration->body);
std::string method_name(method->name);
if (method->name == kConstructMethodName) {
signature.parameter_types.types.insert(
signature.parameter_types.types.begin() + signature.implicit_count,
constructor_this_type);
// Constructor
if (!signature.return_type->IsVoid()) {
ReportError("constructors musn't have a return type");
......@@ -263,23 +264,23 @@ void DeclarationVisitor::DeclareMethods(
ReportError("constructors musn't have labels");
}
method_name = kConstructMethodName;
Declarations::CreateMethod(constructor_this_type, method_name, signature,
false, body);
} else {
signature.parameter_types.types.insert(
signature.parameter_types.types.begin() + signature.implicit_count,
container_type);
signature.return_type = container_type;
ReturnStatement* return_statement = MakeNode<ReturnStatement>(
MakeNode<IdentifierExpression>(kThisParameterName));
body = MakeNode<BlockStatement>(
false, std::vector<Statement*>{body, return_statement});
body = WrapBodyWithNoThrow(body, "exception thrown from constructor");
}
Declarations::CreateMethod(container_type, method_name, signature, false,
body);
}
}
if (container_type->Constructors().size() != 0) return;
// Generate default constructor.
Signature constructor_signature;
constructor_signature.parameter_types.var_args = false;
constructor_signature.return_type = TypeOracle::GetVoidType();
constructor_signature.return_type = container_type;
std::vector<const AggregateType*> hierarchy = container_type->GetHierarchy();
std::vector<Statement*> statements;
......@@ -287,7 +288,8 @@ void DeclarationVisitor::DeclareMethods(
size_t parameter_number = 0;
constructor_signature.parameter_names.push_back(kThisParameterName);
constructor_signature.parameter_types.types.push_back(constructor_this_type);
constructor_signature.parameter_types.types.push_back(container_type);
constructor_signature.implicit_count = 1;
std::vector<Expression*> super_arguments;
for (auto current_type : hierarchy) {
for (auto& f : current_type->fields()) {
......@@ -300,7 +302,13 @@ void DeclarationVisitor::DeclareMethods(
if (container_type != current_type) {
super_arguments.push_back(MakeNode<IdentifierExpression>(
std::vector<std::string>{}, parameter_name));
} else if (container_type->IsClassType()) {
Statement* statement =
MakeNode<ExpressionStatement>(MakeNode<StoreObjectFieldExpression>(
constructor_this, f.name_and_type.name, value));
initializer_statements.push_back(statement);
} else {
DCHECK(container_type->IsStructType());
LocationExpression* location = MakeNode<FieldAccessExpression>(
constructor_this, f.name_and_type.name);
Statement* statement = MakeNode<ExpressionStatement>(
......@@ -313,20 +321,26 @@ void DeclarationVisitor::DeclareMethods(
if (hierarchy.size() > 1) {
IdentifierExpression* super_identifier = MakeNode<IdentifierExpression>(
std::vector<std::string>{}, kSuperMethodName);
Statement* statement =
Statement* super_call_statement =
MakeNode<ExpressionStatement>(MakeNode<CallMethodExpression>(
constructor_this, super_identifier, super_arguments,
std::vector<std::string>{}));
statements.push_back(statement);
statements.push_back(super_call_statement);
}
for (auto s : initializer_statements) {
statements.push_back(s);
}
statements.push_back(MakeNode<ReturnStatement>(MakeNode<IdentifierExpression>(
std::vector<std::string>{}, kThisParameterName)));
Statement* constructor_body = MakeNode<BlockStatement>(false, statements);
Declarations::CreateMethod(constructor_this_type, kConstructMethodName,
constructor_body = WrapBodyWithNoThrow(constructor_body,
"exception thrown from constructor");
Declarations::CreateMethod(container_type, kConstructMethodName,
constructor_signature, false, constructor_body);
}
......@@ -334,11 +348,8 @@ void DeclarationVisitor::Visit(StructDeclaration* decl) {
std::vector<Field> fields;
size_t offset = 0;
for (auto& field : decl->fields) {
const Type* field_type = Declarations::GetType(field.name_and_type.type);
fields.push_back({field.name_and_type.type->pos,
{field.name_and_type.name, field_type},
offset,
false});
const Type* field_type = Declarations::GetType(field.type);
fields.push_back({{field.name, field_type}, offset, false});
offset += LoweredSlotCount(field_type);
}
StructType* struct_type = Declarations::DeclareStruct(decl->name, fields);
......@@ -349,15 +360,18 @@ void DeclarationVisitor::Visit(ClassDeclaration* decl) {
// Compute the offset of the class' first member. If the class extends
// another class, it's the size of the extended class, otherwise zero.
size_t first_field_offset = 0;
const Type* super_type = Declarations::LookupType(decl->super);
if (decl->extends) {
const Type* super_type = Declarations::LookupType(*decl->extends);
if (super_type != TypeOracle::GetTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError("class \"", decl->name,
ReportError(
"class \"", decl->name,
"\" must extend either Tagged or an already declared class");
}
first_field_offset = super_class->size();
}
}
// The generates clause must create a TNode<>
std::string generates = decl->name;
......@@ -371,7 +385,7 @@ void DeclarationVisitor::Visit(ClassDeclaration* decl) {
}
std::vector<Field> fields;
size_t class_offset = first_field_offset;
size_t offset = first_field_offset;
bool seen_strong = false;
bool seen_weak = false;
for (ClassFieldExpression& field : decl->fields) {
......@@ -398,15 +412,13 @@ void DeclarationVisitor::Visit(ClassDeclaration* decl) {
"field \"", field.name_and_type.name, "\" of class \"", decl->name,
"\" must be a subtype of Tagged (other types not yet supported)");
}
fields.push_back({field.name_and_type.type->pos,
{field.name_and_type.name, field_type},
class_offset,
field.weak});
class_offset += kTaggedSize;
fields.push_back(
{{field.name_and_type.name, field_type}, offset, field.weak});
offset += kTaggedSize;
}
auto new_class = Declarations::DeclareClass(
super_type, decl->name, decl->transient, generates, fields, class_offset);
decl->extends, decl->name, decl->transient, generates, fields, offset);
DeclareMethods(new_class, decl->methods);
// For each field, construct AST snippits that implement a CSA accessor
......
......@@ -174,43 +174,16 @@ StructType* Declarations::DeclareStruct(const std::string& name,
return new_type;
}
ClassType* Declarations::DeclareClass(const Type* super_class,
ClassType* Declarations::DeclareClass(base::Optional<std::string> parent,
const std::string& name, bool transient,
const std::string& generates,
std::vector<Field> fields, size_t size) {
std::vector<Field> this_struct_fields;
size_t struct_offset = 0;
const StructType* super_struct_type = nullptr;
// In order to ensure "atomicity" of object allocation, a class'
// constructors operate on a per-class internal struct rather than the class
// directly until the constructor has successfully completed and all class
// members are available. Create the appropriate struct type for use in the
// class' constructors, including a '_super' field in the struct that
// contains the values constructed by calls to super constructors.
if (super_class->IsClassType()) {
super_struct_type = ClassType::cast(super_class)->struct_type();
this_struct_fields.push_back(
{CurrentSourcePosition::Get(),
{kConstructorStructSuperFieldName, super_struct_type},
struct_offset,
false});
struct_offset += LoweredSlotCount(super_struct_type);
}
for (auto& field : fields) {
const Type* field_type = field.name_and_type.type;
this_struct_fields.push_back({field.pos,
{field.name_and_type.name, field_type},
struct_offset,
false});
struct_offset += LoweredSlotCount(field_type);
const Type* parent_type = nullptr;
if (parent) {
parent_type = LookupType(QualifiedName{*parent});
}
StructType* this_struct_type = DeclareStruct(
kClassConstructorThisStructPrefix + name, this_struct_fields);
ClassType* new_type =
TypeOracle::GetClassType(super_class, name, transient, generates,
std::move(fields), this_struct_type, size);
this_struct_type->SetDerivedFrom(new_type);
ClassType* new_type = TypeOracle::GetClassType(
parent_type, name, transient, generates, std::move(fields), size);
DeclareType(name, new_type, false);
return new_type;
}
......
......@@ -84,8 +84,9 @@ class Declarations {
static StructType* DeclareStruct(const std::string& name,
const std::vector<Field>& fields);
static ClassType* DeclareClass(const Type* super, const std::string& name,
bool transient, const std::string& generates,
static ClassType* DeclareClass(base::Optional<std::string> parent,
const std::string& name, bool transient,
const std::string& generates,
std::vector<Field> fields, size_t size);
static Macro* CreateMacro(std::string external_name,
......
......@@ -195,16 +195,14 @@ VisitResult ImplementationVisitor::InlineMacro(
const Type* return_type = macro->signature().return_type;
bool can_return = return_type != TypeOracle::GetNeverType();
CurrentConstructorInfo::Scope current_constructor;
if (macro->IsConstructor())
CurrentConstructorInfo::Get() = ConstructorInfo{0};
BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
BlockBindings<LocalLabel> label_bindings(&LabelBindingsManager::Get());
DCHECK_EQ(macro->signature().parameter_names.size(),
arguments.size() + (this_reference ? 1 : 0));
DCHECK_EQ(this_reference.has_value(), macro->IsMethod());
{
size_t i = 0;
// Bind the this for methods. Methods that modify a struct-type "this" must
// only be called if the this is in a variable, in which case the
// LocalValue is non-const. Otherwise, the LocalValue used for the parameter
......@@ -217,14 +215,14 @@ VisitResult ImplementationVisitor::InlineMacro(
LocalValue this_value = LocalValue{!this_reference->IsVariableAccess(),
this_reference->GetVisitResult()};
parameter_bindings.Add(kThisParameterName, this_value);
i++;
}
size_t i = 0;
for (auto arg : arguments) {
if (this_reference && i == signature.implicit_count) i++;
const std::string& name = macro->parameter_names()[i++];
parameter_bindings.Add(name, LocalValue{true, arg});
}
}
DCHECK_EQ(label_blocks.size(), signature.labels.size());
for (size_t i = 0; i < signature.labels.size(); ++i) {
......@@ -243,16 +241,6 @@ VisitResult ImplementationVisitor::InlineMacro(
SetReturnValue(VisitResult(return_type,
stack.TopRange(lowered_return_types.size())));
}
// The stack copy used to initialize the _macro_end block is only used
// as a template for the actual gotos generated by return statements. It
// doesn't correspond to any real return values, and thus shouldn't contain
// top types, because these would pollute actual return value types that get
// unioned with them for return statements, erroneously forcing them to top.
for (auto i = stack.begin(); i != stack.end(); ++i) {
if ((*i)->IsTopType()) {
*i = TopType::cast(*i)->source_type();
}
}
macro_end = assembler().NewBlock(std::move(stack));
macro_end_binding.emplace(&LabelBindingsManager::Get(), "_macro_end",
LocalLabel{macro_end, {return_type}});
......@@ -295,11 +283,6 @@ VisitResult ImplementationVisitor::InlineMacro(
}
void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
// Do not generate code for inlined macros.
if (macro->ShouldBeInlined()) {
return;
}
CurrentCallable::Scope current_callable(macro);
const Signature& signature = macro->signature();
const Type* return_type = macro->signature().return_type;
......@@ -324,6 +307,7 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
std::vector<VisitResult> arguments;
size_t i = 0;
base::Optional<LocationReference> this_reference;
if (Method* method = Method::DynamicCast(macro)) {
const Type* this_type = method->aggregate_type();
......@@ -335,10 +319,10 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
// Mark the this as a temporary to prevent assignment to it.
this_reference =
LocationReference::Temporary(this_result, "this parameter");
++i;
}
for (size_t i = 0; i < macro->signature().parameter_names.size(); ++i) {
if (this_reference && i == macro->signature().implicit_count) continue;
for (; i < macro->signature().parameter_names.size(); ++i) {
const std::string& name = macro->parameter_names()[i];
std::string external_name = ExternalParameterName(name);
const Type* type = macro->signature().types()[i];
......@@ -405,11 +389,19 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
void ImplementationVisitor::Visit(Macro* macro) {
if (macro->IsExternal()) return;
// Do not generate code for inlined macros.
if (macro->ShouldBeInlined()) return;
VisitMacroCommon(macro);
}
void ImplementationVisitor::Visit(Method* method) {
DCHECK(!method->IsExternal());
// Do not generate code for inlined methods, they have to be inlined in order
// to get reference semantics for the 'this' argument.
if (method->ShouldBeInlined()) return;
CurrentConstructorInfo::Scope current_constructor;
if (method->IsConstructor())
CurrentConstructorInfo::Get() = ConstructorInfo{0, false};
VisitMacroCommon(method);
}
......@@ -1118,29 +1110,6 @@ const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
return TypeOracle::GetVoidType();
}
VisitResult ImplementationVisitor::TemporaryUninitializedStruct(
const StructType* struct_type, const std::string& reason) {
StackRange range = assembler().TopRange(0);
for (const Field& f : struct_type->fields()) {
if (const StructType* struct_type =
StructType::DynamicCast(f.name_and_type.type)) {
range.Extend(
TemporaryUninitializedStruct(struct_type, reason).stack_range());
} else {
std::string descriptor = "unitialized field '" + f.name_and_type.name +
"' declared at " + PositionAsString(f.pos) +
" (" + reason + ")";
TypeVector lowered_types = LowerType(f.name_and_type.type);
for (const Type* type : lowered_types) {
assembler().Emit(PushUninitializedInstruction{
TypeOracle::GetTopType(descriptor, type)});
}
range.Extend(assembler().TopRange(lowered_types.size()));
}
}
return VisitResult(struct_type, range);
}
VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
size_t parameter_count = expr->label_block->parameters.names.size();
std::vector<VisitResult> parameters;
......@@ -1227,70 +1196,23 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
ReportError("type for new expression must be a class, \"", *type,
"\" is not");
}
// In order to ensure "atomicity" of object allocation, a class' constructors
// operate on a per-class internal struct rather than the class directly until
// the constructor has successfully completed and all class members are
// available. Create the appropriate unitialized struct and pass it to the
// matching class constructor with the arguments that were passed to new{}
StructType* class_this_struct = class_type->struct_type();
VisitResult unitialized_struct = TemporaryUninitializedStruct(
class_this_struct,
"it's not set in the constructor for class " + class_type->name());
Arguments constructor_arguments;
for (auto p : expr->parameters) {
constructor_arguments.parameters.push_back(Visit(p));
}
LocationReference unitialized_struct_ref =
LocationReference::VariableAccess(unitialized_struct);
Callable* callable =
LookupConstructor(unitialized_struct_ref, constructor_arguments, {});
GenerateCall(callable, unitialized_struct_ref, constructor_arguments,
{class_type}, false);
VisitResult new_struct_result = unitialized_struct;
// Output the code to generate an unitialized object of the class size in the
// GC heap.
Arguments allocate_arguments;
allocate_arguments.parameters.push_back(VisitResult(
TypeOracle::GetConstInt31Type(), std::to_string(class_type->size())));
VisitResult allocate_result =
GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
DCHECK(allocate_result.IsOnStack());
// Fill in the fields of the newly allocated class by copying the values
// from the struct that was built by the constructor. So that the generaeted
// code is a bit more readable, assign the values from the first class
// member to the last, in order. To do this, first build a list of fields
// to assign to in reverse order by visiting the class heirarchy.
std::vector<std::pair<const Field*, VisitResult>> store_pairs;
const ClassType* current_class = class_type;
while (current_class != nullptr) {
auto& fields = current_class->fields();
for (auto i = fields.rbegin(); i != fields.rend(); ++i) {
store_pairs.push_back(std::make_pair(
&*i, ProjectStructField(new_struct_result, i->name_and_type.name)));
}
current_class = current_class->GetSuperClass();
if (current_class) {
new_struct_result = ProjectStructField(new_struct_result,
kConstructorStructSuperFieldName);
}
}
// Now that the reversed list of fields and the assignment VisitResults are
// available, emit the copies in reverse order of the reversed list to
// produce the class field assignments in the expected order.
for (auto i = store_pairs.rbegin(); i != store_pairs.rend(); ++i) {
assembler().Emit(
PeekInstruction(allocate_result.stack_range().begin(), class_type));
assembler().Emit(PeekInstruction(i->second.stack_range().begin(),
i->first->name_and_type.type));
assembler().Emit(
StoreObjectFieldInstruction(class_type, i->first->name_and_type.name));
Arguments constructor_arguments;
for (auto p : expr->parameters) {
constructor_arguments.parameters.push_back(Visit(p));
}
return stack_scope.Yield(allocate_result);
Callable* callable =
LookupCall({{}, kConstructMethodName}, class_type->Constructors(),
constructor_arguments, {});
return stack_scope.Yield(
GenerateCall(callable,
LocationReference::Temporary(allocate_result,
"unitialized object from new"),
constructor_arguments, {class_type}, false));
}
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
......@@ -1435,15 +1357,17 @@ void ImplementationVisitor::GenerateFunctionDeclaration(
namespace {
void FailCallableLookup(const std::string& reason, const QualifiedName& name,
const TypeVector& parameter_types,
const std::vector<Binding<LocalLabel>*>& labels,
const Arguments& arguments,
const std::vector<Signature>& candidates) {
std::stringstream stream;
stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")";
if (labels.size() != 0) {
stream << "\n"
<< reason << ": \n " << name << "("
<< arguments.parameters.GetTypeVector() << ")";
if (arguments.labels.size() != 0) {
stream << " labels ";
for (size_t i = 0; i < labels.size(); ++i) {
stream << labels[i]->name() << "(" << labels[i]->parameter_types << ")";
for (size_t i = 0; i < arguments.labels.size(); ++i) {
stream << arguments.labels[i]->name() << "("
<< arguments.labels[i]->parameter_types << ")";
}
}
stream << "\ncandidates are:";
......@@ -1492,12 +1416,11 @@ Block* ImplementationVisitor::LookupSimpleLabel(const std::string& name) {
}
template <class Container>
Callable* ImplementationVisitor::LookupCallable(
Callable* ImplementationVisitor::LookupCall(
const QualifiedName& name, const Container& declaration_container,
const TypeVector& parameter_types,
const std::vector<Binding<LocalLabel>*>& labels,
const TypeVector& specialization_types) {
const Arguments& arguments, const TypeVector& specialization_types) {
Callable* result = nullptr;
TypeVector parameter_types(arguments.parameters.GetTypeVector());
std::vector<Declarable*> overloads;
std::vector<Signature> overload_signatures;
......@@ -1520,7 +1443,7 @@ Callable* ImplementationVisitor::LookupCallable(
std::vector<size_t> candidates;
for (size_t i = 0; i < overloads.size(); ++i) {
const Signature& signature = overload_signatures[i];
bool try_bool_context = labels.size() == 0 &&
bool try_bool_context = arguments.labels.size() == 0 &&
signature.return_type == TypeOracle::GetNeverType();
base::Optional<Binding<LocalLabel>*> true_label;
base::Optional<Binding<LocalLabel>*> false_label;
......@@ -1528,7 +1451,7 @@ Callable* ImplementationVisitor::LookupCallable(
true_label = TryLookupLabel(kTrueLabelName);
false_label = TryLookupLabel(kFalseLabelName);
}
if (IsCompatibleSignature(signature, parameter_types, labels) ||
if (IsCompatibleSignature(signature, parameter_types, arguments.labels) ||
(true_label && false_label &&
IsCompatibleSignature(signature, parameter_types,
{*true_label, *false_label}))) {
......@@ -1542,7 +1465,7 @@ Callable* ImplementationVisitor::LookupCallable(
ReportError(stream.str());
} else if (candidates.empty()) {
FailCallableLookup("cannot find suitable callable with name", name,
parameter_types, labels, overload_signatures);
arguments, overload_signatures);
}
auto is_better_candidate = [&](size_t a, size_t b) {
......@@ -1562,7 +1485,7 @@ Callable* ImplementationVisitor::LookupCallable(
for (size_t i : candidates) {
candidate_signatures.push_back(overload_signatures[i]);
}
FailCallableLookup("ambiguous callable ", name, parameter_types, labels,
FailCallableLookup("ambiguous callable", name, arguments,
candidate_signatures);
}
}
......@@ -1590,27 +1513,6 @@ Callable* ImplementationVisitor::LookupCallable(
return result;
}
template <class Container>
Callable* ImplementationVisitor::LookupCallable(
const QualifiedName& name, const Container& declaration_container,
const Arguments& arguments, const TypeVector& specialization_types) {
return LookupCallable(name, declaration_container,
arguments.parameters.GetTypeVector(), arguments.labels,
specialization_types);
}
Method* ImplementationVisitor::LookupMethod(
const std::string& name, LocationReference this_reference,
const Arguments& arguments, const TypeVector& specialization_types) {
TypeVector types(arguments.parameters.GetTypeVector());
types.insert(types.begin(), this_reference.GetVisitResult().type());
return Method::cast(
LookupCallable({{}, name},
AggregateType::cast(this_reference.GetVisitResult().type())
->Methods(name),
types, arguments.labels, specialization_types));
}
const Type* ImplementationVisitor::GetCommonType(const Type* left,
const Type* right) {
const Type* common_type;
......@@ -1643,19 +1545,31 @@ VisitResult ImplementationVisitor::Visit(StructExpression* decl) {
ReportError(s.str());
}
const StructType* struct_type = StructType::cast(raw_type);
// Push unitialized 'this'
VisitResult uninitialized_struct = TemporaryUninitializedStruct(
struct_type,
"it's not set in the constructor for struct " + struct_type->name());
if (struct_type->fields().size() != decl->expressions.size()) {
std::stringstream s;
s << "initializer count mismatch for struct " << decl->name << " (expected "
<< struct_type->fields().size() << ", found " << decl->expressions.size()
<< ")";
ReportError(s.str());
}
// Push uninitialized 'this'
TypeVector lowered_types = LowerType(struct_type);
for (const Type* type : lowered_types) {
assembler().Emit(PushUninitializedInstruction{TypeOracle::GetTopType(
"unitialized struct field at " + PositionAsString(decl->pos), type)});
}
VisitResult uninitialized_struct =
VisitResult(struct_type, assembler().TopRange(lowered_types.size()));
Arguments constructor_arguments;
for (auto p : decl->expressions) {
constructor_arguments.parameters.push_back(Visit(p));
}
LocationReference this_ref =
LocationReference::VariableAccess(uninitialized_struct);
Callable* callable = LookupConstructor(this_ref, constructor_arguments, {});
GenerateCall(callable, this_ref, constructor_arguments, {}, false);
return stack_scope.Yield(uninitialized_struct);
Callable* callable =
LookupCall({{}, kConstructMethodName}, struct_type->Constructors(),
constructor_arguments, {});
return stack_scope.Yield(GenerateCall(
callable, LocationReference::VariableAccess(uninitialized_struct),
constructor_arguments, {}, false));
}
LocationReference ImplementationVisitor::GetLocationReference(
......@@ -1701,6 +1615,11 @@ LocationReference ImplementationVisitor::GetLocationReference(
LocationReference ImplementationVisitor::GetLocationReference(
IdentifierExpression* expr) {
if (expr->namespace_qualification.empty()) {
if (expr->IsThis()) {
if (CurrentConstructorInfo::Get()) {
CurrentConstructorInfo::Get()->accessed_this = true;
}
}
if (base::Optional<Binding<LocalValue>*> value =
TryLookupLocalValue(expr->name)) {
if (expr->generic_arguments.size() != 0) {
......@@ -1878,21 +1797,6 @@ VisitResult ImplementationVisitor::GenerateCall(
std::vector<std::string> constexpr_arguments;
size_t current = 0;
for (; current < callable->signature().implicit_count; ++current) {
std::string implicit_name = callable->signature().parameter_names[current];
base::Optional<Binding<LocalValue>*> val =
TryLookupLocalValue(implicit_name);
if (!val) {
ReportError("implicit parameter '", implicit_name,
"' required for call to '", callable->ReadableName(),
"' is not defined");
}
AddCallParameter(callable, (*val)->value,
callable->signature().parameter_types.types[current],
&converted_arguments, &argument_range,
&constexpr_arguments);
}
if (this_reference) {
DCHECK(callable->IsMethod());
Method* method = Method::cast(callable);
......@@ -1902,8 +1806,7 @@ VisitResult ImplementationVisitor::GenerateCall(
if (method->ShouldBeInlined()) {
if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
ReportError("this parameter must be a subtype of ",
*method->aggregate_type(), " but it is of type ",
this_value.type());
*method->aggregate_type());
}
} else {
AddCallParameter(callable, this_value, method->aggregate_type(),
......@@ -1913,6 +1816,21 @@ VisitResult ImplementationVisitor::GenerateCall(
++current;
}
for (; current < callable->signature().implicit_count; ++current) {
std::string implicit_name = callable->signature().parameter_names[current];
base::Optional<Binding<LocalValue>*> val =
TryLookupLocalValue(implicit_name);
if (!val) {
ReportError("implicit parameter '", implicit_name,
"' required for call to '", callable->ReadableName(),
"' is not defined");
}
AddCallParameter(callable, (*val)->value,
callable->signature().parameter_types.types[current],
&converted_arguments, &argument_range,
&constexpr_arguments);
}
for (auto arg : arguments.parameters) {
const Type* to_type = (current >= callable->signature().types().size())
? TypeOracle::GetObjectType()
......@@ -2098,8 +2016,8 @@ VisitResult ImplementationVisitor::GenerateCall(
const QualifiedName& callable_name, Arguments arguments,
const TypeVector& specialization_types, bool is_tailcall) {
Callable* callable =
LookupCallable(callable_name, Declarations::Lookup(callable_name),
arguments, specialization_types);
LookupCall(callable_name, Declarations::Lookup(callable_name), arguments,
specialization_types);
return GenerateCall(callable, base::nullopt, arguments, specialization_types,
is_tailcall);
}
......@@ -2132,35 +2050,17 @@ VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
std::string method_name = expr->method->name;
TypeVector specialization_types =
GetTypeVector(expr->method->generic_arguments);
LocationReference target = GetLocationReference(expr->target);
if (!target.IsVariableAccess()) {
VisitResult result = GenerateFetchFromLocation(target);
target = LocationReference::Temporary(result, "method target result");
}
const AggregateType* target_type =
AggregateType::DynamicCast(target.GetVisitResult().type());
if (!target_type) {
ReportError("target of method call not a struct or class type");
}
if (method_name == kConstructMethodName || method_name == kSuperMethodName) {
if (CurrentConstructorInfo::Get()) {
ConstructorInfo& info = *CurrentConstructorInfo::Get();
if (method_name == kSuperMethodName) {
if (info.accessed_this) {
ReportError("cannot call super after accessing \"this\"");
}
if (info.super_calls != 0) {
ReportError("\"super\" can only be called once from a constructor");
}
++info.super_calls;
CHECK(target_type->IsStructType());
base::Optional<const ClassType*> derived_from =
StructType::cast(target_type)->GetDerivedFrom();
if (!derived_from) {
ReportError("\"super\" can only be called from class constructors");
}
if ((*derived_from)->GetSuperClass() == nullptr) {
ReportError(
"\"super\" can only be called in constructors for derived "
"classes");
}
} else {
ReportError("cannot call a constructor from a constructor");
}
......@@ -2169,26 +2069,41 @@ VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
"cannot call a constructor or \"super\" from a non-constructor");
}
}
for (Expression* arg : expr->arguments) {
arguments.parameters.push_back(Visit(arg));
const AggregateType* target_type;
LocationReference target = GetLocationReference(expr->target);
if (target.IsVariableAccess()) {
target_type = AggregateType::DynamicCast(target.variable().type());
} else {
VisitResult result = GenerateFetchFromLocation(target);
target = LocationReference::Temporary(result, "method target result");
target_type = AggregateType::DynamicCast(result.type());
}
if (!target_type) {
ReportError("target of method call not a struct or class type");
}
for (Expression* arg : expr->arguments)
arguments.parameters.push_back(Visit(arg));
arguments.labels = LabelsFromIdentifiers(expr->labels);
TypeVector argument_types = arguments.parameters.GetTypeVector();
DCHECK_EQ(expr->method->namespace_qualification.size(), 0);
QualifiedName qualified_name = QualifiedName(method_name);
Callable* callable = nullptr;
if (method_name == kConstructMethodName) {
callable = LookupConstructor(target, arguments, {});
callable =
LookupCall(qualified_name, target_type->Constructors(), arguments, {});
} else if (method_name == kSuperMethodName) {
LocationReference super_this =
LocationReference::VariableAccess(ProjectStructField(
target.GetVisitResult(), kConstructorStructSuperFieldName));
callable = LookupConstructor(super_this, arguments, {});
VisitResult super_result =
GenerateCall(callable, super_this, arguments, {}, false);
return scope.Yield(super_result);
if (!target_type->IsClassType()) {
ReportError("cannot call super in struct constructor");
}
target_type = ClassType::cast(target_type)->GetSuperClass();
if (!target_type) {
ReportError("cannot call \"super\" on a class with no superclass");
}
callable = LookupCall(QualifiedName(kConstructMethodName),
target_type->Constructors(), arguments, {});
} else {
callable = LookupMethod(method_name, target, arguments, {});
callable = LookupCall(qualified_name, target_type->Methods(method_name),
arguments, {});
}
return scope.Yield(GenerateCall(callable, target, arguments, {}, false));
}
......
......@@ -212,8 +212,6 @@ class ImplementationVisitor : public FileVisitor {
VisitResult Visit(Expression* expr);
const Type* Visit(Statement* stmt);
VisitResult TemporaryUninitializedStruct(const StructType* struct_type,
const std::string& reason);
VisitResult Visit(StructExpression* decl);
LocationReference GetLocationReference(Expression* location);
......@@ -292,6 +290,7 @@ class ImplementationVisitor : public FileVisitor {
struct ConstructorInfo {
int super_calls;
bool accessed_this;
};
DECLARE_CONTEXTUAL_VARIABLE(ValueBindingsManager,
......@@ -397,29 +396,11 @@ class ImplementationVisitor : public FileVisitor {
Binding<LocalLabel>* LookupLabel(const std::string& name);
Block* LookupSimpleLabel(const std::string& name);
template <class Container>
Callable* LookupCallable(const QualifiedName& name,
Callable* LookupCall(const QualifiedName& name,
const Container& declaration_container,
const TypeVector& types,
const std::vector<Binding<LocalLabel>*>& labels,
const TypeVector& specialization_types);
template <class Container>
Callable* LookupCallable(const QualifiedName& name,
const Container& declaration_container,
const Arguments& arguments,
const TypeVector& specialization_types);
Method* LookupMethod(const std::string& name, LocationReference target,
const Arguments& arguments,
const TypeVector& specialization_types);
Method* LookupConstructor(LocationReference target,
const Arguments& arguments,
const TypeVector& specialization_types) {
return LookupMethod(kConstructMethodName, target, arguments,
specialization_types);
}
const Type* GetCommonType(const Type* left, const Type* right);
VisitResult GenerateCopy(const VisitResult& to_copy);
......
......@@ -42,10 +42,8 @@ enum class ParseResultHolderBase::TypeId {
kOptionalLabelBlockPtr,
kNameAndTypeExpression,
kClassFieldExpression,
kStructFieldExpression,
kStdVectorOfNameAndTypeExpression,
kStdVectorOfClassFieldExpression,
kStdVectorOfStructFieldExpression,
kIncrementDecrementOperator,
kOptionalStdString,
kStdVectorOfStatementPtr,
......@@ -109,10 +107,6 @@ V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<ClassFieldExpression>::id =
ParseResultTypeId::kClassFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<StructFieldExpression>::id =
ParseResultTypeId::kStructFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<NameAndTypeExpression>>::id =
ParseResultTypeId::kStdVectorOfNameAndTypeExpression;
......@@ -121,10 +115,6 @@ V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<ClassFieldExpression>>::id =
ParseResultTypeId::kStdVectorOfClassFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<StructFieldExpression>>::id =
ParseResultTypeId::kStdVectorOfStructFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<IncrementDecrementOperator>::id =
ParseResultTypeId::kIncrementDecrementOperator;
......@@ -591,7 +581,7 @@ base::Optional<ParseResult> MakeClassDeclaration(
if (!IsValidTypeName(name)) {
NamingConventionError("Type", name, "UpperCamelCase");
}
auto extends = child_results->NextAs<std::string>();
auto extends = child_results->NextAs<base::Optional<std::string>>();
auto generates = child_results->NextAs<base::Optional<std::string>>();
auto methods = child_results->NextAs<std::vector<Declaration*>>();
auto fields = child_results->NextAs<std::vector<ClassFieldExpression>>();
......@@ -633,7 +623,7 @@ base::Optional<ParseResult> MakeStructDeclaration(
ParseResultIterator* child_results) {
auto name = child_results->NextAs<std::string>();
auto methods = child_results->NextAs<std::vector<Declaration*>>();
auto fields = child_results->NextAs<std::vector<StructFieldExpression>>();
auto fields = child_results->NextAs<std::vector<NameAndTypeExpression>>();
Declaration* result = MakeNode<StructDeclaration>(
std::move(name), std::move(methods), std::move(fields));
return ParseResult{result};
......@@ -1129,13 +1119,6 @@ base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
return ParseResult{ClassFieldExpression{{std::move(name), type}, weak}};
}
base::Optional<ParseResult> MakeStructField(
ParseResultIterator* child_results) {
auto name = child_results->NextAs<std::string>();
auto type = child_results->NextAs<TypeExpression*>();
return ParseResult{StructFieldExpression{{std::move(name), type}}};
}
base::Optional<ParseResult> ExtractAssignmentOperator(
ParseResultIterator* child_results) {
auto op = child_results->NextAs<std::string>();
......@@ -1326,9 +1309,6 @@ struct TorqueGrammar : Grammar {
Rule({CheckIf(Token("weak")), &identifier, Token(":"), &type, Token(";")},
MakeClassField)};
Symbol structField = {
Rule({&identifier, Token(":"), &type, Token(";")}, MakeStructField)};
// Result: ParameterList
Symbol parameterListNoVararg = {
Rule({optionalImplicitParameterList, Token("("),
......@@ -1609,7 +1589,7 @@ struct TorqueGrammar : Grammar {
&externalString, Token(";")},
MakeExternConstDeclaration),
Rule({CheckIf(Token("transient")), Token("class"), &identifier,
Sequence({Token("extends"), &identifier}),
Optional<std::string>(Sequence({Token("extends"), &identifier})),
Optional<std::string>(
Sequence({Token("generates"), &externalString})),
Token("{"), List<Declaration*>(&method),
......@@ -1617,7 +1597,8 @@ struct TorqueGrammar : Grammar {
MakeClassDeclaration),
Rule({Token("struct"), &identifier, Token("{"),
List<Declaration*>(&method),
List<StructFieldExpression>(&structField), Token("}")},
List<NameAndTypeExpression>(Sequence({&nameAndType, Token(";")})),
Token("}")},
MakeStructDeclaration),
Rule({CheckIf(Token("transient")), Token("type"), &identifier,
Optional<std::string>(Sequence({Token("extends"), &identifier})),
......
......@@ -38,10 +38,9 @@ class TypeOracle : public ContextualClass<TypeOracle> {
static ClassType* GetClassType(const Type* parent, const std::string& name,
bool transient, const std::string& generates,
const std::vector<Field>& fields,
StructType* this_struct, size_t size) {
ClassType* result =
new ClassType(parent, CurrentNamespace(), name, transient, generates,
fields, this_struct, size);
size_t size) {
ClassType* result = new ClassType(parent, CurrentNamespace(), name,
transient, generates, fields, size);
Get().struct_types_.push_back(std::unique_ptr<ClassType>(result));
return result;
}
......
......@@ -184,38 +184,6 @@ const Type* SubtractType(const Type* a, const Type* b) {
return TypeOracle::GetUnionType(result);
}
AggregateType::AggregateType(Kind kind, const Type* parent, Namespace* nspace,
const std::string& name,
const std::vector<Field>& fields)
: Type(kind, parent), namespace_(nspace), name_(name), fields_(fields) {
// Check the aggregate hierarchy and currently defined class for duplicate
// field declarations.
auto hierarchy = GetHierarchy();
std::map<std::string, const AggregateType*> field_names;
for (const AggregateType* aggregate_type : hierarchy) {
for (const Field& field : aggregate_type->fields()) {
const std::string& field_name = field.name_and_type.name;
auto i = field_names.find(field_name);
if (i != field_names.end()) {
CurrentSourcePosition::Scope current_source_position(field.pos);
std::string aggregate_type_name =
aggregate_type->IsClassType() ? "class" : "struct";
if (i->second == this) {
ReportError(aggregate_type_name, " '", name,
"' declares a field with the name '", field_name,
"' more than once");
} else {
ReportError(aggregate_type_name, " '", name,
"' declares a field with the name '", field_name,
"' that masks an inherited field from class '",
i->second->name(), "'");
}
}
field_names[field_name] = aggregate_type;
}
}
}
std::vector<const AggregateType*> AggregateType::GetHierarchy() {
std::vector<const AggregateType*> hierarchy;
const AggregateType* current_container_type = this;
......@@ -249,7 +217,7 @@ std::string StructType::GetGeneratedTypeName() const {
std::vector<Method*> AggregateType::Methods(const std::string& name) const {
std::vector<Method*> result;
std::copy_if(methods_.begin(), methods_.end(), std::back_inserter(result),
[name](Macro* macro) { return macro->ReadableName() == name; });
[&](Macro* macro) { return macro->ReadableName() == name; });
return result;
}
......@@ -375,53 +343,19 @@ bool operator<(const Type& a, const Type& b) {
return a.MangledName() < b.MangledName();
}
VisitResult ProjectStructField(const StructType* original_struct,
VisitResult structure,
VisitResult ProjectStructField(VisitResult structure,
const std::string& fieldname) {
DCHECK(structure.IsOnStack());
BottomOffset begin = structure.stack_range().begin();
// Check constructor this super classes for fields.
const StructType* type = StructType::cast(structure.type());
auto& fields = type->fields();
for (auto& field : fields) {
for (auto& field : type->fields()) {
BottomOffset end = begin + LoweredSlotCount(field.name_and_type.type);
if (field.name_and_type.name == fieldname) {
return VisitResult(field.name_and_type.type, StackRange{begin, end});
}
begin = end;
}
if (fields.size() > 0 &&
fields[0].name_and_type.name == kConstructorStructSuperFieldName) {
structure = ProjectStructField(original_struct, structure,
kConstructorStructSuperFieldName);
return ProjectStructField(original_struct, structure, fieldname);
} else {
base::Optional<const ClassType*> class_type =
original_struct->GetDerivedFrom();
if (original_struct == type) {
if (class_type) {
ReportError("class '", (*class_type)->name(),
"' doesn't contain a field '", fieldname, "'");
} else {
ReportError("struct '", original_struct->name(),
"' doesn't contain a field '", fieldname, "'");
}
} else {
DCHECK(class_type);
ReportError(
"class '", (*class_type)->name(),
"' or one of its derived-from classes doesn't contain a field '",
fieldname, "'");
}
}
}
VisitResult ProjectStructField(VisitResult structure,
const std::string& fieldname) {
DCHECK(structure.IsOnStack());
const StructType* type = StructType::cast(structure.type());
return ProjectStructField(type, structure, fieldname);
UNREACHABLE();
}
namespace {
......
......@@ -12,7 +12,6 @@
#include <vector>
#include "src/base/optional.h"
#include "src/torque/source-positions.h"
#include "src/torque/utils.h"
namespace v8 {
......@@ -44,7 +43,6 @@ static const char* const CONST_FLOAT64_TYPE_STRING = "constexpr float64";
class Macro;
class Method;
class StructType;
class ClassType;
class Value;
class Namespace;
......@@ -152,7 +150,6 @@ struct NameAndType {
std::ostream& operator<<(std::ostream& os, const NameAndType& name_and_type);
struct Field {
SourcePosition pos;
NameAndType name_and_type;
size_t offset;
bool is_weak;
......@@ -409,7 +406,8 @@ class AggregateType : public Type {
protected:
AggregateType(Kind kind, const Type* parent, Namespace* nspace,
const std::string& name, const std::vector<Field>& fields);
const std::string& name, const std::vector<Field>& fields)
: Type(kind, parent), namespace_(nspace), name_(name), fields_(fields) {}
private:
Namespace* namespace_;
......@@ -424,13 +422,6 @@ class StructType final : public AggregateType {
std::string ToExplicitString() const override;
std::string GetGeneratedTypeName() const override;
void SetDerivedFrom(const ClassType* derived_from) {
derived_from_ = derived_from;
}
base::Optional<const ClassType*> GetDerivedFrom() const {
return derived_from_;
}
private:
friend class TypeOracle;
StructType(Namespace* nspace, const std::string& name,
......@@ -438,8 +429,6 @@ class StructType final : public AggregateType {
: AggregateType(Kind::kStructType, nullptr, nspace, name, fields) {}
const std::string& GetStructName() const { return name(); }
base::Optional<const ClassType*> derived_from_;
};
class ClassType final : public AggregateType {
......@@ -452,9 +441,7 @@ class ClassType final : public AggregateType {
std::string GetGeneratedTNodeTypeName() const override;
bool IsTransient() const override { return transient_; }
size_t size() const { return size_; }
StructType* struct_type() const { return this_struct_; }
const ClassType* GetSuperClass() const {
if (parent() == nullptr) return nullptr;
return parent()->IsClassType() ? ClassType::DynamicCast(parent()) : nullptr;
}
......@@ -462,15 +449,12 @@ class ClassType final : public AggregateType {
friend class TypeOracle;
ClassType(const Type* parent, Namespace* nspace, const std::string& name,
bool transient, const std::string& generates,
const std::vector<Field>& fields, StructType* this_struct,
size_t size)
const std::vector<Field>& fields, size_t size)
: AggregateType(Kind::kClassType, parent, nspace, name, fields),
this_struct_(this_struct),
transient_(transient),
size_(size),
generates_(generates) {}
StructType* this_struct_;
bool transient_;
size_t size_;
const std::string generates_;
......
......@@ -272,8 +272,6 @@ constexpr int kTaggedSize = sizeof(void*);
static const char* const kConstructMethodName = "constructor";
static const char* const kSuperMethodName = "super";
static const char* const kConstructorStructSuperFieldName = "_super";
static const char* const kClassConstructorThisStructPrefix = "_ThisStruct";
// Erase elements of a container that has a constant-time erase function, like
// std::set or std::list. Calling this on std::vector would have quadratic
......
......@@ -736,36 +736,10 @@ namespace test {
c: int32;
}
struct TestCustomStructConstructor {
constructor(x: int32, y: Smi) {
this.a = x;
this.c = x;
this.b = y;
this.d = y;
}
a: int32;
b: Smi;
c: int32;
d: Smi;
}
macro TestStructConstructor(implicit context: Context)() {
// Test default constructor
let a: TestOuter = TestOuter{5, TestInner{6, 7}, 8};
assert(a.a == 5);
assert(a.b.x == 6);
assert(a.b.y == 7);
assert(a.c == 8);
a.b.x = 1;
assert(a.b.x == 1);
let a: TestOuter = TestOuter{0, TestInner{0, 0}, 0};
a.b.SetX(2);
assert(a.b.x == 2);
assert(a.b.GetX() == 2);
// Test custom constructor
let w: TestCustomStructConstructor = TestCustomStructConstructor{1, 2};
assert(w.a == 1);
assert(w.b == 2);
assert(w.c == 1);
assert(w.d == 2);
}
}
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