Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix various issues with object literals #1235

Merged
merged 5 commits into from
Apr 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 116 additions & 104 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ import {
PropertyPrototype,
IndexSignature,
File,
mangleInternalName
mangleInternalName,
DeclaredElement
} from "./program";

import {
Expand Down Expand Up @@ -8601,46 +8602,36 @@ export class Compiler extends DiagnosticEmitter {
private compileObjectLiteral(expression: ObjectLiteralExpression, contextualType: Type): ExpressionRef {
var module = this.module;

// contextual type must be a class
// Check that contextual type is a class (TODO: hidden class for interfaces?)
var classReference = contextualType.classReference;
if (!classReference || classReference.is(CommonFlags.ABSTRACT)) {
if (!contextualType.is(TypeFlags.REFERENCE) || !classReference || classReference.kind != ElementKind.CLASS) {
this.error(
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
expression.range, "<object>", contextualType.toString()
);
return module.unreachable();
}
var classType = classReference.type;
this.currentType = classType.nonNullableType;
if (classReference.is(CommonFlags.ABSTRACT)) {
this.error(
DiagnosticCode.Cannot_create_an_instance_of_an_abstract_class,
expression.range
);
return module.unreachable();
}

// if present, check that the constructor is compatible with object literals
var ctor = classReference.constructorInstance;
if (ctor) {
// TODO: if the constructor requires parameters, check whether these are given as part of the
// object literal and use them to call the ctor while not generating a store.
if (ctor.signature.requiredParameters) {
this.error(
DiagnosticCode.Constructor_of_class_0_must_not_require_any_arguments,
expression.range, classReference.toString()
);
return module.unreachable();
}
if (ctor.is(CommonFlags.PRIVATE)) {
this.error(
DiagnosticCode.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration,
expression.range, classReference.toString()
);
return module.unreachable();
}
if (ctor.is(CommonFlags.PROTECTED)) {
this.error(
DiagnosticCode.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration,
expression.range, classReference.toString()
);
return module.unreachable();
}
if (ctor.hasDecorator(DecoratorFlags.UNSAFE)) this.checkUnsafe(expression);
// Check that the class is compatible with object literals
var ctorPrototype = classReference.prototype.constructorPrototype;
if (ctorPrototype) {
this.errorRelated(
DiagnosticCode.Class_0_cannot_declare_a_constructor_when_instantiated_from_an_object_literal,
expression.range, ctorPrototype.identifierNode.range, classType.toString()
);
return module.unreachable();
}

var isManaged = classReference.type.isManaged;
var isManaged = classType.isManaged;
if (!isManaged) {
this.checkUnsafe(expression, findDecorator(DecoratorKind.UNMANAGED, classReference.decoratorNodes));
}
Expand All @@ -8654,29 +8645,47 @@ export class Compiler extends DiagnosticEmitter {
var exprs = new Array<ExpressionRef>();
var flow = this.currentFlow;
var tempLocal = isManaged
? flow.getAutoreleaseLocal(classReference.type)
: flow.getTempLocal(classReference.type);
? flow.getAutoreleaseLocal(classType)
: flow.getTempLocal(classType);
var nativeClassType = classType.toNativeType();
assert(numNames == values.length);

// Assume all class fields will be omitted, and add them to our omitted list
let omittedClassFieldMembers = new Set<string>();
var omittedFields = new Set<Field>();
if (members) {
for (let _keys = Map_keys(members), i = 0, k = _keys.length; i < k; ++i) {
let memberKey = _keys[i];
let member = assert(members.get(memberKey));
if (member !== null && member.kind == ElementKind.FIELD) {
omittedClassFieldMembers.add(member.name);
omittedFields.add(<Field>member); // incl. private/protected
}
}
}

// Iterate through the members defined in our expression
for (let i = 0, k = numNames; i < k; ++i) {
let member = members ? members.get(names[i].text) : null;
if (!member || member.kind != ElementKind.FIELD) {
for (let i = 0; i < numNames; ++i) {
let memberName = names[i].text;
let member: DeclaredElement;
if (!members || !members.has(memberName) || (member = assert(members.get(memberName))).kind != ElementKind.FIELD) {
this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1,
names[i].range, names[i].text, classReference.toString()
names[i].range, memberName, classType.toString()
);
hasErrors = true;
continue;
}
if (member.is(CommonFlags.PRIVATE)) {
this.error(
DiagnosticCode.Property_0_is_private_and_only_accessible_within_class_1,
names[i].range, memberName, classType.toString()
);
hasErrors = true;
continue;
}
if (member.is(CommonFlags.PROTECTED)) {
this.error(
DiagnosticCode.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
names[i].range, memberName, classType.toString()
);
hasErrors = true;
continue;
Expand All @@ -8685,95 +8694,98 @@ export class Compiler extends DiagnosticEmitter {
let fieldType = fieldInstance.type;

let expr = this.compileExpression(values[i], fieldType, Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN);
if (isManaged && fieldType.isManaged && !this.skippedAutoreleases.has(expr)) {
if (fieldType.isManaged && !this.skippedAutoreleases.has(expr)) {
expr = this.makeRetain(expr);
}
exprs.push(this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
expr,
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
exprs.push(
module.store( // TODO: handle setters as well
fieldType.byteSize,
module.local_get(tempLocal.index, nativeClassType),
expr,
fieldType.toNativeType(),
fieldInstance.memoryOffset
)
);

// This member is no longer omitted, so delete from our omitted fields
omittedClassFieldMembers.delete(member.name);
omittedFields.delete(fieldInstance);
}
this.currentType = classReference.type.nonNullableType;
this.currentType = classType.nonNullableType;
if (hasErrors) return module.unreachable();

// Iterate through the remaining omittedClassFieldMembers.
if (members) {
// Check remaining omitted fields
for (let _values = Set_values(omittedFields), j = 0, l = _values.length; j < l; ++j) {
let fieldInstance = _values[j];
let fieldType = fieldInstance.type;

for (let _values = Set_values(omittedClassFieldMembers), j = 0, l = _values.length; j < l; ++j) {
let omittedMemberKey = _values[j];
let member = assert(members.get(omittedMemberKey));

let fieldInstance = <Field>member;
let fieldType = fieldInstance.type;
if (fieldInstance.initializerNode) {
continue; // set by default ctor
}

if (fieldType.is(TypeFlags.REFERENCE) && fieldType.classReference !== null) {
// TODO: Check if it is a class, with a default value (constructor with no params).
if (!fieldType.is(TypeFlags.NULLABLE)) {
this.error(
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
expression.range, "<object>", classReference.toString()
);
hasErrors = true;
continue;
}
if (fieldType.is(TypeFlags.REFERENCE) && fieldType.classReference !== null) {
// TODO: Check if it is a class, with a default value (constructor with no params).
if (!fieldType.is(TypeFlags.NULLABLE)) {
this.error(
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
expression.range, fieldInstance.name, "<object>", classType.toString()
);
hasErrors = true;
continue;
}
}

switch(fieldType.kind) {
// Number Types (and Number alias types)
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.USIZE:
case TypeKind.ISIZE:
case TypeKind.BOOL:
case TypeKind.I64:
case TypeKind.U64:
case TypeKind.F32:
case TypeKind.F64: {
exprs.push(this.module.store( // TODO: handle setters as well
switch (fieldType.kind) {
// Number Types (and Number alias types)
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32:
case TypeKind.I64:
case TypeKind.ISIZE:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.U64:
case TypeKind.USIZE:
case TypeKind.BOOL:
case TypeKind.F32:
case TypeKind.F64: {
exprs.push(
module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
module.local_get(tempLocal.index, nativeClassType),
this.makeZero(fieldType),
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
continue;
}
)
);
continue;
}

// Otherwise, error
this.error(
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
expression.range, "<object>", classReference.toString()
);
hasErrors = true;
}

// Otherwise error
this.error(
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
expression.range, fieldInstance.name, "<object>", classType.toString()
);
hasErrors = true;
}
if (hasErrors) return module.unreachable();

// allocate a new instance first and assign 'this' to the temp. local
exprs.unshift(module.local_set(
tempLocal.index,
isManaged
? this.makeRetain(this.makeAllocation(classReference))
: this.makeAllocation(classReference)
));
exprs.unshift(
module.local_set(tempLocal.index,
this.compileInstantiate(classReference, [], Constraints.WILL_RETAIN, expression)
)
);

// once all field values have been set, return 'this'
exprs.push(module.local_get(tempLocal.index, this.options.nativeSizeType));
exprs.push(
module.local_get(tempLocal.index, nativeClassType)
);

if (!isManaged) flow.freeTempLocal(tempLocal);
this.currentType = classReference.type;
return module.flatten(exprs, this.options.nativeSizeType);
this.currentType = classType.nonNullableType;
return module.flatten(exprs, nativeClassType);
}

private compileNewExpression(
Expand Down Expand Up @@ -8949,7 +8961,7 @@ export class Compiler extends DiagnosticEmitter {
this.makeZero(this.options.usizeType),
constraints
);
if (getExpressionType(expr) != NativeType.None) { // possibly IMM_DROPPED
if (getExpressionType(expr) != NativeType.None) { // possibly WILL_DROP
this.currentType = classInstance.type; // important because a super ctor could be called
}
return expr;
Expand Down
10 changes: 8 additions & 2 deletions src/diagnosticMessages.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export enum DiagnosticCode {
Duplicate_decorator = 213,
Type_0_is_illegal_in_this_context = 214,
Optional_parameter_must_have_an_initializer = 215,
Constructor_of_class_0_must_not_require_any_arguments = 216,
Class_0_cannot_declare_a_constructor_when_instantiated_from_an_object_literal = 216,
Function_0_cannot_be_inlined_into_itself = 217,
Cannot_access_method_0_without_calling_it_as_it_requires_this_to_be_set = 218,
Optional_properties_are_not_supported = 219,
Expand Down Expand Up @@ -116,6 +116,7 @@ export enum DiagnosticCode {
_super_can_only_be_referenced_in_a_derived_class = 2335,
Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors = 2337,
Property_0_does_not_exist_on_type_1 = 2339,
Property_0_is_private_and_only_accessible_within_class_1 = 2341,
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
This_expression_is_not_constructable = 2351,
A_function_whose_declared_type_is_not_void_must_return_a_value = 2355,
Expand All @@ -131,12 +132,14 @@ export enum DiagnosticCode {
Duplicate_function_implementation = 2393,
Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local = 2395,
A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged = 2434,
Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses = 2445,
The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly = 2453,
Type_0_has_no_property_1 = 2460,
The_0_operator_cannot_be_applied_to_type_1 = 2469,
In_const_enum_declarations_member_initializer_must_be_constant_expression = 2474,
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
_0_is_referenced_directly_or_indirectly_in_its_own_base_expression = 2506,
Cannot_create_an_instance_of_an_abstract_class = 2511,
Object_is_possibly_null = 2531,
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
Expand Down Expand Up @@ -185,7 +188,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 213: return "Duplicate decorator.";
case 214: return "Type '{0}' is illegal in this context.";
case 215: return "Optional parameter must have an initializer.";
case 216: return "Constructor of class '{0}' must not require any arguments.";
case 216: return "Class '{0}' cannot declare a constructor when instantiated from an object literal.";
case 217: return "Function '{0}' cannot be inlined into itself.";
case 218: return "Cannot access method '{0}' without calling it as it requires 'this' to be set.";
case 219: return "Optional properties are not supported.";
Expand Down Expand Up @@ -272,6 +275,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2335: return "'super' can only be referenced in a derived class.";
case 2337: return "Super calls are not permitted outside constructors or in nested functions inside constructors.";
case 2339: return "Property '{0}' does not exist on type '{1}'.";
case 2341: return "Property '{0}' is private and only accessible within class '{1}'.";
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
case 2351: return "This expression is not constructable.";
case 2355: return "A function whose declared type is not 'void' must return a value.";
Expand All @@ -287,12 +291,14 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2393: return "Duplicate function implementation.";
case 2395: return "Individual declarations in merged declaration '{0}' must be all exported or all local.";
case 2434: return "A namespace declaration cannot be located prior to a class or function with which it is merged.";
case 2445: return "Property '{0}' is protected and only accessible within class '{1}' and its subclasses.";
case 2453: return "The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.";
case 2460: return "Type '{0}' has no property '{1}'.";
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
case 2474: return "In 'const' enum declarations member initializer must be constant expression.";
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
case 2506: return "'{0}' is referenced directly or indirectly in its own base expression.";
case 2511: return "Cannot create an instance of an abstract class.";
case 2531: return "Object is possibly 'null'.";
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
case 2541: return "The target of an assignment must be a variable or a property access.";
Expand Down
Loading