Skip to content

Commit 75328f3

Browse files
authored
Implement calls to 'super()' (#445)
1 parent 65c4acd commit 75328f3

10 files changed

+832
-14
lines changed

src/ast.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ export function nodeIsCallable(kind: NodeKind): bool {
120120
case NodeKind.CALL:
121121
case NodeKind.ELEMENTACCESS:
122122
case NodeKind.PARENTHESIZED:
123-
case NodeKind.PROPERTYACCESS: return true;
123+
case NodeKind.PROPERTYACCESS:
124+
case NodeKind.SUPER: return true;
124125
}
125126
return false;
126127
}

src/compiler.ts

+87-2
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,8 @@ export class Compiler extends DiagnosticEmitter {
10951095
if (isConstructor) {
10961096
let nativeSizeType = this.options.nativeSizeType;
10971097
assert(instance.is(CommonFlags.INSTANCE));
1098+
let parent = assert(instance.parent);
1099+
assert(parent.kind == ElementKind.CLASS);
10981100

10991101
// implicitly return `this` if the constructor doesn't always return on its own
11001102
if (!flow.is(FlowFlags.RETURNS)) {
@@ -1105,14 +1107,20 @@ export class Compiler extends DiagnosticEmitter {
11051107

11061108
// if not all branches are guaranteed to allocate, also append a conditional allocation
11071109
} else {
1108-
let parent = assert(instance.parent);
1109-
assert(parent.kind == ElementKind.CLASS);
11101110
stmts.push(module.createTeeLocal(0,
11111111
this.makeConditionalAllocate(<Class>parent, declaration.name)
11121112
));
11131113
}
11141114
}
11151115

1116+
// check that super has been called if this is a derived class
1117+
if ((<Class>parent).base && !flow.is(FlowFlags.CALLS_SUPER)) {
1118+
this.error(
1119+
DiagnosticCode.Constructors_for_derived_classes_must_contain_a_super_call,
1120+
instance.prototype.declaration.range
1121+
);
1122+
}
1123+
11161124
// make sure all branches return
11171125
} else if (returnType != Type.void && !flow.is(FlowFlags.RETURNS)) {
11181126
this.error(
@@ -5230,6 +5238,43 @@ export class Compiler extends DiagnosticEmitter {
52305238
break;
52315239
}
52325240

5241+
case ElementKind.CLASS: {
5242+
5243+
// call to `super()`
5244+
if (expression.expression.kind == NodeKind.SUPER) {
5245+
if (!currentFunction.is(CommonFlags.CONSTRUCTOR)) {
5246+
this.error(
5247+
DiagnosticCode.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors,
5248+
expression.range
5249+
);
5250+
return module.createUnreachable();
5251+
}
5252+
5253+
let classInstance = assert(currentFunction.parent);
5254+
assert(classInstance.kind == ElementKind.CLASS);
5255+
let expr = this.compileSuperInstantiate(<Class>classInstance, expression.arguments, expression);
5256+
this.currentType = Type.void;
5257+
5258+
// check that super() is called before allocation is performed (incl. in super arguments)
5259+
let flow = currentFunction.flow;
5260+
if (flow.isAny(
5261+
FlowFlags.ALLOCATES |
5262+
FlowFlags.CONDITIONALLY_ALLOCATES
5263+
)) {
5264+
this.error(
5265+
DiagnosticCode._super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class,
5266+
expression.range
5267+
);
5268+
return module.createUnreachable();
5269+
}
5270+
flow.set(FlowFlags.ALLOCATES | FlowFlags.CALLS_SUPER);
5271+
5272+
let thisLocal = assert(this.currentFunction.flow.getScopedLocal("this"));
5273+
return module.createSetLocal(thisLocal.index, expr);
5274+
}
5275+
// otherwise fall-through
5276+
}
5277+
52335278
// not supported
52345279
default: {
52355280
this.error(
@@ -6027,6 +6072,15 @@ export class Compiler extends DiagnosticEmitter {
60276072
return module.createUnreachable();
60286073
}
60296074
case NodeKind.SUPER: {
6075+
if (currentFunction.is(CommonFlags.CONSTRUCTOR)) {
6076+
if (!currentFunction.flow.is(FlowFlags.CALLS_SUPER)) {
6077+
// TS1034 in the parser effectively limits this to property accesses
6078+
this.error(
6079+
DiagnosticCode._super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class,
6080+
expression.range
6081+
);
6082+
}
6083+
}
60306084
let flow = currentFunction.flow;
60316085
if (flow.is(FlowFlags.INLINE_CONTEXT)) {
60326086
let scopedThis = flow.getScopedLocal("this");
@@ -6658,6 +6712,37 @@ export class Compiler extends DiagnosticEmitter {
66586712
return expr;
66596713
}
66606714

6715+
compileSuperInstantiate(classInstance: Class, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
6716+
// traverse to the top-most visible constructor (except the current one)
6717+
var currentClassInstance: Class | null = classInstance.base;
6718+
var constructorInstance: Function | null = null;
6719+
while (currentClassInstance) {
6720+
constructorInstance = currentClassInstance.constructorInstance;
6721+
if (constructorInstance) break; // TODO: check visibility
6722+
currentClassInstance = currentClassInstance.base;
6723+
}
6724+
6725+
// if a constructor is present, allocate the necessary memory for `this` and call it
6726+
var expr: ExpressionRef;
6727+
if (constructorInstance) {
6728+
expr = this.compileCallDirect(constructorInstance, argumentExpressions, reportNode,
6729+
this.makeAllocate(classInstance, reportNode)
6730+
);
6731+
6732+
// otherwise simply allocate a new instance and initialize its fields
6733+
} else {
6734+
if (argumentExpressions.length) {
6735+
this.error(
6736+
DiagnosticCode.Expected_0_arguments_but_got_1,
6737+
reportNode.range, "0", argumentExpressions.length.toString(10)
6738+
);
6739+
}
6740+
expr = this.makeAllocate(classInstance, reportNode);
6741+
}
6742+
this.currentType = classInstance.type;
6743+
return expr;
6744+
}
6745+
66616746
compileParenthesizedExpression(
66626747
expression: ParenthesizedExpression,
66636748
contextualType: Type

src/diagnosticMessages.generated.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export enum DiagnosticCode {
6666
Unexpected_end_of_text = 1126,
6767
Invalid_character = 1127,
6868
_case_or_default_expected = 1130,
69+
_super_must_be_followed_by_an_argument_list_or_member_access = 1034,
6970
A_declare_modifier_cannot_be_used_in_an_already_ambient_context = 1038,
7071
Type_argument_expected = 1140,
7172
String_literal_expected = 1141,
@@ -94,13 +95,16 @@ export enum DiagnosticCode {
9495
Index_signature_is_missing_in_type_0 = 2329,
9596
_this_cannot_be_referenced_in_current_location = 2332,
9697
_super_can_only_be_referenced_in_a_derived_class = 2335,
98+
Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors = 2337,
9799
Property_0_does_not_exist_on_type_1 = 2339,
98100
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
99101
Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature = 2351,
100102
A_function_whose_declared_type_is_not_void_must_return_a_value = 2355,
101103
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
102104
The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access = 2364,
103105
Operator_0_cannot_be_applied_to_types_1_and_2 = 2365,
106+
A_super_call_must_be_the_first_statement_in_the_constructor = 2376,
107+
Constructors_for_derived_classes_must_contain_a_super_call = 2377,
104108
_get_and_set_accessor_must_have_the_same_type = 2380,
105109
Constructor_implementation_is_missing = 2390,
106110
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
@@ -124,7 +128,9 @@ export enum DiagnosticCode {
124128
Required_type_parameters_may_not_follow_optional_type_parameters = 2706,
125129
File_0_not_found = 6054,
126130
Numeric_separators_are_not_allowed_here = 6188,
127-
Multiple_consecutive_numeric_separators_are_not_permitted = 6189
131+
Multiple_consecutive_numeric_separators_are_not_permitted = 6189,
132+
_super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class = 17009,
133+
_super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class = 17011
128134
}
129135

130136
/** Translates a diagnostic code to its respective string. */
@@ -189,6 +195,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
189195
case 1126: return "Unexpected end of text.";
190196
case 1127: return "Invalid character.";
191197
case 1130: return "'case' or 'default' expected.";
198+
case 1034: return "'super' must be followed by an argument list or member access.";
192199
case 1038: return "A 'declare' modifier cannot be used in an already ambient context.";
193200
case 1140: return "Type argument expected.";
194201
case 1141: return "String literal expected.";
@@ -217,13 +224,16 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
217224
case 2329: return "Index signature is missing in type '{0}'.";
218225
case 2332: return "'this' cannot be referenced in current location.";
219226
case 2335: return "'super' can only be referenced in a derived class.";
227+
case 2337: return "Super calls are not permitted outside constructors or in nested functions inside constructors.";
220228
case 2339: return "Property '{0}' does not exist on type '{1}'.";
221229
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
222230
case 2351: return "Cannot use 'new' with an expression whose type lacks a construct signature.";
223231
case 2355: return "A function whose declared type is not 'void' must return a value.";
224232
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
225233
case 2364: return "The left-hand side of an assignment expression must be a variable or a property access.";
226234
case 2365: return "Operator '{0}' cannot be applied to types '{1}' and '{2}'.";
235+
case 2376: return "A 'super' call must be the first statement in the constructor.";
236+
case 2377: return "Constructors for derived classes must contain a 'super' call.";
227237
case 2380: return "'get' and 'set' accessor must have the same type.";
228238
case 2390: return "Constructor implementation is missing.";
229239
case 2391: return "Function implementation is missing or not immediately following the declaration.";
@@ -248,6 +258,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
248258
case 6054: return "File '{0}' not found.";
249259
case 6188: return "Numeric separators are not allowed here.";
250260
case 6189: return "Multiple consecutive numeric separators are not permitted.";
261+
case 17009: return "'super' must be called before accessing 'this' in the constructor of a derived class.";
262+
case 17011: return "'super' must be called before accessing a property of 'super' in the constructor of a derived class.";
251263
default: return "";
252264
}
253265
}

src/diagnosticMessages.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"Unexpected end of text.": 1126,
6060
"Invalid character.": 1127,
6161
"'case' or 'default' expected.": 1130,
62+
"'super' must be followed by an argument list or member access.": 1034,
6263
"A 'declare' modifier cannot be used in an already ambient context.": 1038,
6364
"Type argument expected.": 1140,
6465
"String literal expected.": 1141,
@@ -88,13 +89,16 @@
8889
"Index signature is missing in type '{0}'.": 2329,
8990
"'this' cannot be referenced in current location.": 2332,
9091
"'super' can only be referenced in a derived class.": 2335,
92+
"Super calls are not permitted outside constructors or in nested functions inside constructors.": 2337,
9193
"Property '{0}' does not exist on type '{1}'.": 2339,
9294
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": 2349,
9395
"Cannot use 'new' with an expression whose type lacks a construct signature.": 2351,
9496
"A function whose declared type is not 'void' must return a value.": 2355,
9597
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
9698
"The left-hand side of an assignment expression must be a variable or a property access.": 2364,
9799
"Operator '{0}' cannot be applied to types '{1}' and '{2}'.": 2365,
100+
"A 'super' call must be the first statement in the constructor.": 2376,
101+
"Constructors for derived classes must contain a 'super' call.": 2377,
98102
"'get' and 'set' accessor must have the same type.": 2380,
99103
"Constructor implementation is missing.": 2390,
100104
"Function implementation is missing or not immediately following the declaration.": 2391,
@@ -119,5 +123,7 @@
119123

120124
"File '{0}' not found.": 6054,
121125
"Numeric separators are not allowed here.": 6188,
122-
"Multiple consecutive numeric separators are not permitted.": 6189
126+
"Multiple consecutive numeric separators are not permitted.": 6189,
127+
"'super' must be called before accessing 'this' in the constructor of a derived class.": 17009,
128+
"'super' must be called before accessing a property of 'super' in the constructor of a derived class.": 17011
123129
}

src/parser.ts

+6
Original file line numberDiff line numberDiff line change
@@ -3337,6 +3337,12 @@ export class Parser extends DiagnosticEmitter {
33373337
return Node.createConstructorExpression(tn.range(startPos, tn.pos));
33383338
}
33393339
case Token.SUPER: {
3340+
if (tn.peek() != Token.DOT && tn.nextToken != Token.OPENPAREN) {
3341+
this.error(
3342+
DiagnosticCode._super_must_be_followed_by_an_argument_list_or_member_access,
3343+
tn.range()
3344+
);
3345+
}
33403346
return Node.createSuperExpression(tn.range(startPos, tn.pos));
33413347
}
33423348
case Token.STRINGLITERAL: {

src/program.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -3041,26 +3041,28 @@ export const enum FlowFlags {
30413041
CONTINUES = 1 << 4,
30423042
/** This branch always allocates. Constructors only. */
30433043
ALLOCATES = 1 << 5,
3044+
/** This branch always calls super. Constructors only. */
3045+
CALLS_SUPER = 1 << 6,
30443046

30453047
// conditional
30463048

30473049
/** This branch conditionally returns in a child branch. */
3048-
CONDITIONALLY_RETURNS = 1 << 6,
3050+
CONDITIONALLY_RETURNS = 1 << 7,
30493051
/** This branch conditionally throws in a child branch. */
3050-
CONDITIONALLY_THROWS = 1 << 7,
3052+
CONDITIONALLY_THROWS = 1 << 8,
30513053
/** This branch conditionally breaks in a child branch. */
3052-
CONDITIONALLY_BREAKS = 1 << 8,
3054+
CONDITIONALLY_BREAKS = 1 << 9,
30533055
/** This branch conditionally continues in a child branch. */
3054-
CONDITIONALLY_CONTINUES = 1 << 9,
3056+
CONDITIONALLY_CONTINUES = 1 << 10,
30553057
/** This branch conditionally allocates in a child branch. Constructors only. */
3056-
CONDITIONALLY_ALLOCATES = 1 << 10,
3058+
CONDITIONALLY_ALLOCATES = 1 << 11,
30573059

30583060
// special
30593061

30603062
/** This branch is part of inlining a function. */
3061-
INLINE_CONTEXT = 1 << 11,
3063+
INLINE_CONTEXT = 1 << 12,
30623064
/** This branch explicitly requests no bounds checking. */
3063-
UNCHECKED_CONTEXT = 1 << 12,
3065+
UNCHECKED_CONTEXT = 1 << 13,
30643066

30653067
// masks
30663068

@@ -3076,7 +3078,8 @@ export const enum FlowFlags {
30763078
| FlowFlags.THROWS
30773079
| FlowFlags.BREAKS
30783080
| FlowFlags.CONTINUES
3079-
| FlowFlags.ALLOCATES,
3081+
| FlowFlags.ALLOCATES
3082+
| FlowFlags.CALLS_SUPER,
30803083

30813084
/** Any conditional flag. */
30823085
ANY_CONDITIONAL = FlowFlags.CONDITIONALLY_RETURNS

src/resolver.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1242,8 +1242,16 @@ export class Resolver extends DiagnosticEmitter {
12421242

12431243
// Lay out fields in advance
12441244
case ElementKind.FIELD_PROTOTYPE: {
1245-
if (!instance.members) instance.members = new Map();
12461245
let fieldDeclaration = (<FieldPrototype>member).declaration;
1246+
if (!instance.members) instance.members = new Map();
1247+
else if (instance.members.has(member.simpleName)) {
1248+
this.error(
1249+
DiagnosticCode.Duplicate_identifier_0,
1250+
fieldDeclaration.name.range,
1251+
member.simpleName
1252+
);
1253+
break;
1254+
}
12471255
let fieldType: Type | null = null;
12481256
// TODO: handle duplicate non-private fields
12491257
if (!fieldDeclaration.type) {

0 commit comments

Comments
 (0)