Skip to content

Commit e257567

Browse files
authored
Fix various object literal issues (#1235)
1 parent 61b9079 commit e257567

20 files changed

+5604
-6277
lines changed

Diff for: src/compiler.ts

+116-104
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ import {
8686
PropertyPrototype,
8787
IndexSignature,
8888
File,
89-
mangleInternalName
89+
mangleInternalName,
90+
DeclaredElement
9091
} from "./program";
9192

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

8604-
// contextual type must be a class
8605+
// Check that contextual type is a class (TODO: hidden class for interfaces?)
86058606
var classReference = contextualType.classReference;
8606-
if (!classReference || classReference.is(CommonFlags.ABSTRACT)) {
8607+
if (!contextualType.is(TypeFlags.REFERENCE) || !classReference || classReference.kind != ElementKind.CLASS) {
86078608
this.error(
86088609
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
86098610
expression.range, "<object>", contextualType.toString()
86108611
);
86118612
return module.unreachable();
86128613
}
8614+
var classType = classReference.type;
8615+
this.currentType = classType.nonNullableType;
8616+
if (classReference.is(CommonFlags.ABSTRACT)) {
8617+
this.error(
8618+
DiagnosticCode.Cannot_create_an_instance_of_an_abstract_class,
8619+
expression.range
8620+
);
8621+
return module.unreachable();
8622+
}
86138623

8614-
// if present, check that the constructor is compatible with object literals
8615-
var ctor = classReference.constructorInstance;
8616-
if (ctor) {
8617-
// TODO: if the constructor requires parameters, check whether these are given as part of the
8618-
// object literal and use them to call the ctor while not generating a store.
8619-
if (ctor.signature.requiredParameters) {
8620-
this.error(
8621-
DiagnosticCode.Constructor_of_class_0_must_not_require_any_arguments,
8622-
expression.range, classReference.toString()
8623-
);
8624-
return module.unreachable();
8625-
}
8626-
if (ctor.is(CommonFlags.PRIVATE)) {
8627-
this.error(
8628-
DiagnosticCode.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration,
8629-
expression.range, classReference.toString()
8630-
);
8631-
return module.unreachable();
8632-
}
8633-
if (ctor.is(CommonFlags.PROTECTED)) {
8634-
this.error(
8635-
DiagnosticCode.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration,
8636-
expression.range, classReference.toString()
8637-
);
8638-
return module.unreachable();
8639-
}
8640-
if (ctor.hasDecorator(DecoratorFlags.UNSAFE)) this.checkUnsafe(expression);
8624+
// Check that the class is compatible with object literals
8625+
var ctorPrototype = classReference.prototype.constructorPrototype;
8626+
if (ctorPrototype) {
8627+
this.errorRelated(
8628+
DiagnosticCode.Class_0_cannot_declare_a_constructor_when_instantiated_from_an_object_literal,
8629+
expression.range, ctorPrototype.identifierNode.range, classType.toString()
8630+
);
8631+
return module.unreachable();
86418632
}
86428633

8643-
var isManaged = classReference.type.isManaged;
8634+
var isManaged = classType.isManaged;
86448635
if (!isManaged) {
86458636
this.checkUnsafe(expression, findDecorator(DecoratorKind.UNMANAGED, classReference.decoratorNodes));
86468637
}
@@ -8654,29 +8645,47 @@ export class Compiler extends DiagnosticEmitter {
86548645
var exprs = new Array<ExpressionRef>();
86558646
var flow = this.currentFlow;
86568647
var tempLocal = isManaged
8657-
? flow.getAutoreleaseLocal(classReference.type)
8658-
: flow.getTempLocal(classReference.type);
8648+
? flow.getAutoreleaseLocal(classType)
8649+
: flow.getTempLocal(classType);
8650+
var nativeClassType = classType.toNativeType();
86598651
assert(numNames == values.length);
86608652

86618653
// Assume all class fields will be omitted, and add them to our omitted list
8662-
let omittedClassFieldMembers = new Set<string>();
8654+
var omittedFields = new Set<Field>();
86638655
if (members) {
86648656
for (let _keys = Map_keys(members), i = 0, k = _keys.length; i < k; ++i) {
86658657
let memberKey = _keys[i];
86668658
let member = assert(members.get(memberKey));
86678659
if (member !== null && member.kind == ElementKind.FIELD) {
8668-
omittedClassFieldMembers.add(member.name);
8660+
omittedFields.add(<Field>member); // incl. private/protected
86698661
}
86708662
}
86718663
}
86728664

86738665
// Iterate through the members defined in our expression
8674-
for (let i = 0, k = numNames; i < k; ++i) {
8675-
let member = members ? members.get(names[i].text) : null;
8676-
if (!member || member.kind != ElementKind.FIELD) {
8666+
for (let i = 0; i < numNames; ++i) {
8667+
let memberName = names[i].text;
8668+
let member: DeclaredElement;
8669+
if (!members || !members.has(memberName) || (member = assert(members.get(memberName))).kind != ElementKind.FIELD) {
86778670
this.error(
86788671
DiagnosticCode.Property_0_does_not_exist_on_type_1,
8679-
names[i].range, names[i].text, classReference.toString()
8672+
names[i].range, memberName, classType.toString()
8673+
);
8674+
hasErrors = true;
8675+
continue;
8676+
}
8677+
if (member.is(CommonFlags.PRIVATE)) {
8678+
this.error(
8679+
DiagnosticCode.Property_0_is_private_and_only_accessible_within_class_1,
8680+
names[i].range, memberName, classType.toString()
8681+
);
8682+
hasErrors = true;
8683+
continue;
8684+
}
8685+
if (member.is(CommonFlags.PROTECTED)) {
8686+
this.error(
8687+
DiagnosticCode.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
8688+
names[i].range, memberName, classType.toString()
86808689
);
86818690
hasErrors = true;
86828691
continue;
@@ -8685,95 +8694,98 @@ export class Compiler extends DiagnosticEmitter {
86858694
let fieldType = fieldInstance.type;
86868695

86878696
let expr = this.compileExpression(values[i], fieldType, Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN);
8688-
if (isManaged && fieldType.isManaged && !this.skippedAutoreleases.has(expr)) {
8697+
if (fieldType.isManaged && !this.skippedAutoreleases.has(expr)) {
86898698
expr = this.makeRetain(expr);
86908699
}
8691-
exprs.push(this.module.store( // TODO: handle setters as well
8692-
fieldType.byteSize,
8693-
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
8694-
expr,
8695-
fieldType.toNativeType(),
8696-
fieldInstance.memoryOffset
8697-
));
8700+
exprs.push(
8701+
module.store( // TODO: handle setters as well
8702+
fieldType.byteSize,
8703+
module.local_get(tempLocal.index, nativeClassType),
8704+
expr,
8705+
fieldType.toNativeType(),
8706+
fieldInstance.memoryOffset
8707+
)
8708+
);
86988709

86998710
// This member is no longer omitted, so delete from our omitted fields
8700-
omittedClassFieldMembers.delete(member.name);
8711+
omittedFields.delete(fieldInstance);
87018712
}
8702-
this.currentType = classReference.type.nonNullableType;
8713+
this.currentType = classType.nonNullableType;
87038714
if (hasErrors) return module.unreachable();
87048715

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

8708-
for (let _values = Set_values(omittedClassFieldMembers), j = 0, l = _values.length; j < l; ++j) {
8709-
let omittedMemberKey = _values[j];
8710-
let member = assert(members.get(omittedMemberKey));
8711-
8712-
let fieldInstance = <Field>member;
8713-
let fieldType = fieldInstance.type;
8721+
if (fieldInstance.initializerNode) {
8722+
continue; // set by default ctor
8723+
}
87148724

8715-
if (fieldType.is(TypeFlags.REFERENCE) && fieldType.classReference !== null) {
8716-
// TODO: Check if it is a class, with a default value (constructor with no params).
8717-
if (!fieldType.is(TypeFlags.NULLABLE)) {
8718-
this.error(
8719-
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
8720-
expression.range, "<object>", classReference.toString()
8721-
);
8722-
hasErrors = true;
8723-
continue;
8724-
}
8725+
if (fieldType.is(TypeFlags.REFERENCE) && fieldType.classReference !== null) {
8726+
// TODO: Check if it is a class, with a default value (constructor with no params).
8727+
if (!fieldType.is(TypeFlags.NULLABLE)) {
8728+
this.error(
8729+
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
8730+
expression.range, fieldInstance.name, "<object>", classType.toString()
8731+
);
8732+
hasErrors = true;
8733+
continue;
87258734
}
8735+
}
87268736

8727-
switch(fieldType.kind) {
8728-
// Number Types (and Number alias types)
8729-
case TypeKind.I8:
8730-
case TypeKind.I16:
8731-
case TypeKind.I32:
8732-
case TypeKind.U8:
8733-
case TypeKind.U16:
8734-
case TypeKind.U32:
8735-
case TypeKind.USIZE:
8736-
case TypeKind.ISIZE:
8737-
case TypeKind.BOOL:
8738-
case TypeKind.I64:
8739-
case TypeKind.U64:
8740-
case TypeKind.F32:
8741-
case TypeKind.F64: {
8742-
exprs.push(this.module.store( // TODO: handle setters as well
8737+
switch (fieldType.kind) {
8738+
// Number Types (and Number alias types)
8739+
case TypeKind.I8:
8740+
case TypeKind.I16:
8741+
case TypeKind.I32:
8742+
case TypeKind.I64:
8743+
case TypeKind.ISIZE:
8744+
case TypeKind.U8:
8745+
case TypeKind.U16:
8746+
case TypeKind.U32:
8747+
case TypeKind.U64:
8748+
case TypeKind.USIZE:
8749+
case TypeKind.BOOL:
8750+
case TypeKind.F32:
8751+
case TypeKind.F64: {
8752+
exprs.push(
8753+
module.store( // TODO: handle setters as well
87438754
fieldType.byteSize,
8744-
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
8755+
module.local_get(tempLocal.index, nativeClassType),
87458756
this.makeZero(fieldType),
87468757
fieldType.toNativeType(),
87478758
fieldInstance.memoryOffset
8748-
));
8749-
continue;
8750-
}
8759+
)
8760+
);
8761+
continue;
87518762
}
8752-
8753-
// Otherwise, error
8754-
this.error(
8755-
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
8756-
expression.range, "<object>", classReference.toString()
8757-
);
8758-
hasErrors = true;
87598763
}
8764+
8765+
// Otherwise error
8766+
this.error(
8767+
DiagnosticCode.Property_0_is_missing_in_type_1_but_required_in_type_2,
8768+
expression.range, fieldInstance.name, "<object>", classType.toString()
8769+
);
8770+
hasErrors = true;
87608771
}
87618772
if (hasErrors) return module.unreachable();
87628773

87638774
// allocate a new instance first and assign 'this' to the temp. local
8764-
exprs.unshift(module.local_set(
8765-
tempLocal.index,
8766-
isManaged
8767-
? this.makeRetain(this.makeAllocation(classReference))
8768-
: this.makeAllocation(classReference)
8769-
));
8775+
exprs.unshift(
8776+
module.local_set(tempLocal.index,
8777+
this.compileInstantiate(classReference, [], Constraints.WILL_RETAIN, expression)
8778+
)
8779+
);
87708780

87718781
// once all field values have been set, return 'this'
8772-
exprs.push(module.local_get(tempLocal.index, this.options.nativeSizeType));
8782+
exprs.push(
8783+
module.local_get(tempLocal.index, nativeClassType)
8784+
);
87738785

87748786
if (!isManaged) flow.freeTempLocal(tempLocal);
8775-
this.currentType = classReference.type;
8776-
return module.flatten(exprs, this.options.nativeSizeType);
8787+
this.currentType = classType.nonNullableType;
8788+
return module.flatten(exprs, nativeClassType);
87778789
}
87788790

87798791
private compileNewExpression(
@@ -8949,7 +8961,7 @@ export class Compiler extends DiagnosticEmitter {
89498961
this.makeZero(this.options.usizeType),
89508962
constraints
89518963
);
8952-
if (getExpressionType(expr) != NativeType.None) { // possibly IMM_DROPPED
8964+
if (getExpressionType(expr) != NativeType.None) { // possibly WILL_DROP
89538965
this.currentType = classInstance.type; // important because a super ctor could be called
89548966
}
89558967
return expr;

Diff for: src/diagnosticMessages.generated.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export enum DiagnosticCode {
2929
Duplicate_decorator = 213,
3030
Type_0_is_illegal_in_this_context = 214,
3131
Optional_parameter_must_have_an_initializer = 215,
32-
Constructor_of_class_0_must_not_require_any_arguments = 216,
32+
Class_0_cannot_declare_a_constructor_when_instantiated_from_an_object_literal = 216,
3333
Function_0_cannot_be_inlined_into_itself = 217,
3434
Cannot_access_method_0_without_calling_it_as_it_requires_this_to_be_set = 218,
3535
Optional_properties_are_not_supported = 219,
@@ -116,6 +116,7 @@ export enum DiagnosticCode {
116116
_super_can_only_be_referenced_in_a_derived_class = 2335,
117117
Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors = 2337,
118118
Property_0_does_not_exist_on_type_1 = 2339,
119+
Property_0_is_private_and_only_accessible_within_class_1 = 2341,
119120
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
120121
This_expression_is_not_constructable = 2351,
121122
A_function_whose_declared_type_is_not_void_must_return_a_value = 2355,
@@ -131,12 +132,14 @@ export enum DiagnosticCode {
131132
Duplicate_function_implementation = 2393,
132133
Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local = 2395,
133134
A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged = 2434,
135+
Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses = 2445,
134136
The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly = 2453,
135137
Type_0_has_no_property_1 = 2460,
136138
The_0_operator_cannot_be_applied_to_type_1 = 2469,
137139
In_const_enum_declarations_member_initializer_must_be_constant_expression = 2474,
138140
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
139141
_0_is_referenced_directly_or_indirectly_in_its_own_base_expression = 2506,
142+
Cannot_create_an_instance_of_an_abstract_class = 2511,
140143
Object_is_possibly_null = 2531,
141144
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
142145
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
@@ -185,7 +188,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
185188
case 213: return "Duplicate decorator.";
186189
case 214: return "Type '{0}' is illegal in this context.";
187190
case 215: return "Optional parameter must have an initializer.";
188-
case 216: return "Constructor of class '{0}' must not require any arguments.";
191+
case 216: return "Class '{0}' cannot declare a constructor when instantiated from an object literal.";
189192
case 217: return "Function '{0}' cannot be inlined into itself.";
190193
case 218: return "Cannot access method '{0}' without calling it as it requires 'this' to be set.";
191194
case 219: return "Optional properties are not supported.";
@@ -272,6 +275,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
272275
case 2335: return "'super' can only be referenced in a derived class.";
273276
case 2337: return "Super calls are not permitted outside constructors or in nested functions inside constructors.";
274277
case 2339: return "Property '{0}' does not exist on type '{1}'.";
278+
case 2341: return "Property '{0}' is private and only accessible within class '{1}'.";
275279
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
276280
case 2351: return "This expression is not constructable.";
277281
case 2355: return "A function whose declared type is not 'void' must return a value.";
@@ -287,12 +291,14 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
287291
case 2393: return "Duplicate function implementation.";
288292
case 2395: return "Individual declarations in merged declaration '{0}' must be all exported or all local.";
289293
case 2434: return "A namespace declaration cannot be located prior to a class or function with which it is merged.";
294+
case 2445: return "Property '{0}' is protected and only accessible within class '{1}' and its subclasses.";
290295
case 2453: return "The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.";
291296
case 2460: return "Type '{0}' has no property '{1}'.";
292297
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
293298
case 2474: return "In 'const' enum declarations member initializer must be constant expression.";
294299
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
295300
case 2506: return "'{0}' is referenced directly or indirectly in its own base expression.";
301+
case 2511: return "Cannot create an instance of an abstract class.";
296302
case 2531: return "Object is possibly 'null'.";
297303
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
298304
case 2541: return "The target of an assignment must be a variable or a property access.";

0 commit comments

Comments
 (0)