Skip to content

Commit ebae7cb

Browse files
authored
Implement optional type parameters (AssemblyScript#360)
* Add a NATIVE<T> macro type to simplify use of a native WebAssembly type * Add default type parameters for internal helpers for explicit loads and stores * Unify loadUnsafe/loadUnsafeWithOffset etc. into one * Renamed loadUnsafe etc. into just LOAD, like a macro * Implement parsing of index signatures, but ignore them, for properly linting code * Refactor TypedArray<T> to use macros
1 parent d7f4874 commit ebae7cb

39 files changed

+4458
-3888
lines changed

src/ast.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export enum NodeKind {
8787
FIELDDECLARATION,
8888
FUNCTIONDECLARATION,
8989
IMPORTDECLARATION,
90+
INDEXSIGNATUREDECLARATION,
9091
INTERFACEDECLARATION,
9192
METHODDECLARATION,
9293
NAMESPACEDECLARATION,
@@ -181,12 +182,14 @@ export abstract class Node {
181182
static createTypeParameter(
182183
name: IdentifierExpression,
183184
extendsType: TypeNode | null,
185+
defaultType: TypeNode | null,
184186
range: Range
185187
): TypeParameterNode {
186188
var elem = new TypeParameterNode();
187189
elem.range = range;
188190
elem.name = name; name.parent = elem;
189191
elem.extendsType = extendsType; if (extendsType) extendsType.parent = elem;
192+
elem.defaultType = defaultType; if (defaultType) defaultType.parent = elem;
190193
return elem;
191194
}
192195

@@ -871,6 +874,18 @@ export abstract class Node {
871874
return stmt;
872875
}
873876

877+
static createIndexSignatureDeclaration(
878+
keyType: TypeNode,
879+
valueType: CommonTypeNode,
880+
range: Range
881+
): IndexSignatureDeclaration {
882+
var elem = new IndexSignatureDeclaration();
883+
elem.range = range;
884+
elem.keyType = keyType; keyType.parent = elem;
885+
elem.valueType = valueType; valueType.parent = elem;
886+
return elem;
887+
}
888+
874889
static createMethodDeclaration(
875890
name: IdentifierExpression,
876891
typeParameters: TypeParameterNode[] | null,
@@ -1070,6 +1085,8 @@ export class TypeParameterNode extends Node {
10701085
name: IdentifierExpression;
10711086
/** Extended type reference, if any. */
10721087
extendsType: TypeNode | null; // can't be a function
1088+
/** Default type if omitted, if any. */
1089+
defaultType: TypeNode | null; // can't be a function
10731090
}
10741091

10751092
/** Represents the kind of a parameter. */
@@ -1622,6 +1639,16 @@ export abstract class DeclarationStatement extends Statement {
16221639
}
16231640
}
16241641

1642+
/** Represents an index signature declaration. */
1643+
export class IndexSignatureDeclaration extends DeclarationStatement {
1644+
kind = NodeKind.INDEXSIGNATUREDECLARATION;
1645+
1646+
/** Key type. */
1647+
keyType: TypeNode;
1648+
/** Value type. */
1649+
valueType: CommonTypeNode;
1650+
}
1651+
16251652
/** Base class of all variable-like declaration statements. */
16261653
export abstract class VariableLikeDeclarationStatement extends DeclarationStatement {
16271654

src/compiler.ts

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ import {
162162
writeI32,
163163
writeI64,
164164
writeF32,
165-
writeF64
165+
writeF64,
166+
makeMap
166167
} from "./util";
167168

168169
/** Compilation target. */
@@ -619,7 +620,7 @@ export class Compiler extends DiagnosticEmitter {
619620
(noTreeShaking || (isEntry && statement.is(CommonFlags.EXPORT))) &&
620621
!(<ClassDeclaration>statement).isGeneric
621622
) {
622-
this.compileClassDeclaration(<ClassDeclaration>statement, [], null);
623+
this.compileClassDeclaration(<ClassDeclaration>statement, []);
623624
}
624625
break;
625626
}
@@ -958,16 +959,15 @@ export class Compiler extends DiagnosticEmitter {
958959
/** Compiles a top-level function given its declaration. */
959960
compileFunctionDeclaration(
960961
declaration: FunctionDeclaration,
961-
typeArguments: TypeNode[],
962-
contextualTypeArguments: Map<string,Type> | null = null
962+
typeArguments: TypeNode[]
963963
): Function | null {
964964
var element = assert(this.program.elementsLookup.get(declaration.fileLevelInternalName));
965965
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
966966
return this.compileFunctionUsingTypeArguments( // reports
967967
<FunctionPrototype>element,
968968
typeArguments,
969-
contextualTypeArguments,
970-
null, // no outer scope (is top level)
969+
makeMap<string,Type>(),
970+
null,
971971
(<FunctionPrototype>element).declaration.name
972972
);
973973
}
@@ -976,7 +976,7 @@ export class Compiler extends DiagnosticEmitter {
976976
compileFunctionUsingTypeArguments(
977977
prototype: FunctionPrototype,
978978
typeArguments: TypeNode[],
979-
contextualTypeArguments: Map<string,Type> | null,
979+
contextualTypeArguments: Map<string,Type>,
980980
outerScope: Flow | null,
981981
reportNode: Node
982982
): Function | null {
@@ -1234,7 +1234,11 @@ export class Compiler extends DiagnosticEmitter {
12341234
(<ClassPrototype>element).is(CommonFlags.EXPORT)
12351235
) && !(<ClassPrototype>element).is(CommonFlags.GENERIC)
12361236
) {
1237-
this.compileClassUsingTypeArguments(<ClassPrototype>element, []);
1237+
this.compileClassUsingTypeArguments(
1238+
<ClassPrototype>element,
1239+
[],
1240+
makeMap<string,Type>()
1241+
);
12381242
}
12391243
break;
12401244
}
@@ -1252,8 +1256,8 @@ export class Compiler extends DiagnosticEmitter {
12521256
this.compileFunctionUsingTypeArguments(
12531257
<FunctionPrototype>element,
12541258
[],
1255-
null, // no contextual type arguments
1256-
null, // no outer scope
1259+
makeMap<string,Type>(),
1260+
null,
12571261
(<FunctionPrototype>element).declaration.name
12581262
);
12591263
}
@@ -1286,7 +1290,11 @@ export class Compiler extends DiagnosticEmitter {
12861290
switch (element.kind) {
12871291
case ElementKind.CLASS_PROTOTYPE: {
12881292
if (!(<ClassPrototype>element).is(CommonFlags.GENERIC)) {
1289-
this.compileClassUsingTypeArguments(<ClassPrototype>element, []);
1293+
this.compileClassUsingTypeArguments(
1294+
<ClassPrototype>element,
1295+
[],
1296+
makeMap<string,Type>()
1297+
);
12901298
}
12911299
break;
12921300
}
@@ -1302,8 +1310,8 @@ export class Compiler extends DiagnosticEmitter {
13021310
this.compileFunctionUsingTypeArguments(
13031311
<FunctionPrototype>element,
13041312
[],
1305-
null, // no contextual type arguments
1306-
null, // no outer scope
1313+
makeMap<string,Type>(),
1314+
null,
13071315
(<FunctionPrototype>element).declaration.name
13081316
);
13091317
}
@@ -1325,23 +1333,22 @@ export class Compiler extends DiagnosticEmitter {
13251333

13261334
compileClassDeclaration(
13271335
declaration: ClassDeclaration,
1328-
typeArguments: TypeNode[],
1329-
contextualTypeArguments: Map<string,Type> | null = null
1336+
typeArguments: TypeNode[]
13301337
): void {
13311338
var element = assert(this.program.elementsLookup.get(declaration.fileLevelInternalName));
13321339
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
13331340
this.compileClassUsingTypeArguments(
13341341
<ClassPrototype>element,
13351342
typeArguments,
1336-
contextualTypeArguments,
1343+
makeMap<string,Type>(),
13371344
declaration
13381345
);
13391346
}
13401347

13411348
compileClassUsingTypeArguments(
13421349
prototype: ClassPrototype,
13431350
typeArguments: TypeNode[],
1344-
contextualTypeArguments: Map<string,Type> | null = null,
1351+
contextualTypeArguments: Map<string,Type>,
13451352
alternativeReportNode: Node | null = null
13461353
): void {
13471354
var instance = this.resolver.resolveClassInclTypeArguments(
@@ -1372,7 +1379,9 @@ export class Compiler extends DiagnosticEmitter {
13721379
) {
13731380
this.compileFunctionUsingTypeArguments(
13741381
<FunctionPrototype>element,
1375-
[], null, null,
1382+
[],
1383+
makeMap<string,Type>(),
1384+
null,
13761385
(<FunctionPrototype>element).declaration.name
13771386
);
13781387
}
@@ -1383,15 +1392,19 @@ export class Compiler extends DiagnosticEmitter {
13831392
if (getter) {
13841393
this.compileFunctionUsingTypeArguments(
13851394
getter,
1386-
[], null, null,
1395+
[],
1396+
makeMap<string,Type>(),
1397+
null,
13871398
getter.declaration.name
13881399
);
13891400
}
13901401
let setter = (<Property>element).setterPrototype;
13911402
if (setter) {
13921403
this.compileFunctionUsingTypeArguments(
13931404
setter,
1394-
[], null, null,
1405+
[],
1406+
makeMap<string,Type>(),
1407+
null,
13951408
setter.declaration.name
13961409
);
13971410
}
@@ -1413,8 +1426,8 @@ export class Compiler extends DiagnosticEmitter {
14131426
this.compileFunctionUsingTypeArguments(
14141427
<FunctionPrototype>element,
14151428
[],
1416-
instance.contextualTypeArguments,
1417-
null, // no outer scope
1429+
makeMap<string,Type>(instance.contextualTypeArguments),
1430+
null,
14181431
(<FunctionPrototype>element).declaration.name
14191432
);
14201433
}
@@ -1429,15 +1442,19 @@ export class Compiler extends DiagnosticEmitter {
14291442
if (getter) {
14301443
this.compileFunctionUsingTypeArguments(
14311444
getter,
1432-
[], instance.contextualTypeArguments, null,
1445+
[],
1446+
makeMap<string,Type>(instance.contextualTypeArguments),
1447+
null,
14331448
getter.declaration.name
14341449
);
14351450
}
14361451
let setter = (<Property>element).setterPrototype;
14371452
if (setter) {
14381453
this.compileFunctionUsingTypeArguments(
14391454
setter,
1440-
[], instance.contextualTypeArguments, null,
1455+
[],
1456+
makeMap<string,Type>(instance.contextualTypeArguments),
1457+
null,
14411458
setter.declaration.name
14421459
);
14431460
}
@@ -5014,7 +5031,7 @@ export class Compiler extends DiagnosticEmitter {
50145031
instance = this.resolver.resolveFunctionInclTypeArguments(
50155032
prototype,
50165033
typeArguments,
5017-
this.currentFunction.flow.contextualTypeArguments,
5034+
makeMap<string,Type>(this.currentFunction.flow.contextualTypeArguments),
50185035
expression
50195036
);
50205037

@@ -5088,7 +5105,7 @@ export class Compiler extends DiagnosticEmitter {
50885105
instance = this.resolver.resolveFunction(
50895106
prototype,
50905107
resolvedTypeArguments,
5091-
this.currentFunction.flow.contextualTypeArguments
5108+
makeMap<string,Type>(this.currentFunction.flow.contextualTypeArguments)
50925109
);
50935110
if (!instance) return this.module.createUnreachable();
50945111
return this.makeCallDirect(instance, argumentExprs);
@@ -5098,11 +5115,7 @@ export class Compiler extends DiagnosticEmitter {
50985115

50995116
// otherwise resolve the non-generic call as usual
51005117
} else {
5101-
instance = this.resolver.resolveFunction(
5102-
prototype,
5103-
null,
5104-
this.currentFunction.flow.contextualTypeArguments
5105-
);
5118+
instance = this.resolver.resolveFunction(prototype, null);
51065119
}
51075120
if (!instance) return this.module.createUnreachable();
51085121

@@ -5241,7 +5254,7 @@ export class Compiler extends DiagnosticEmitter {
52415254
typeArguments = this.resolver.resolveTypeArguments(
52425255
assert(prototype.declaration.typeParameters),
52435256
typeArgumentNodes,
5244-
this.currentFunction.flow.contextualTypeArguments,
5257+
makeMap<string,Type>(this.currentFunction.flow.contextualTypeArguments),
52455258
expression
52465259
);
52475260
}
@@ -5932,7 +5945,7 @@ export class Compiler extends DiagnosticEmitter {
59325945
var instance = this.compileFunctionUsingTypeArguments(
59335946
prototype,
59345947
[],
5935-
flow.contextualTypeArguments,
5948+
makeMap<string,Type>(flow.contextualTypeArguments),
59365949
flow,
59375950
declaration
59385951
);
@@ -6093,7 +6106,7 @@ export class Compiler extends DiagnosticEmitter {
60936106
let instance = this.resolver.resolveFunction(
60946107
<FunctionPrototype>target,
60956108
null,
6096-
currentFunction.flow.contextualTypeArguments
6109+
makeMap<string,Type>(currentFunction.flow.contextualTypeArguments)
60976110
);
60986111
if (!(instance && this.compileFunction(instance))) return module.createUnreachable();
60996112
let index = this.ensureFunctionTableEntry(instance);
@@ -6438,7 +6451,11 @@ export class Compiler extends DiagnosticEmitter {
64386451

64396452
// create the Array segment and return a pointer to it
64406453
var arrayPrototype = assert(program.arrayPrototype);
6441-
var arrayInstance = assert(this.resolver.resolveClass(arrayPrototype, [ elementType ]));
6454+
var arrayInstance = assert(this.resolver.resolveClass(
6455+
arrayPrototype,
6456+
[ elementType ],
6457+
makeMap<string,Type>()
6458+
));
64426459
var arrayHeaderSize = (arrayInstance.currentMemoryOffset + 7) & ~7;
64436460
if (hasGC) {
64446461
buf = new Uint8Array(gcHeaderSize + arrayHeaderSize);
@@ -6506,9 +6523,11 @@ export class Compiler extends DiagnosticEmitter {
65066523

65076524
// otherwise obtain the array type
65086525
var arrayPrototype = assert(this.program.arrayPrototype);
6509-
if (!arrayPrototype || arrayPrototype.kind != ElementKind.CLASS_PROTOTYPE) return module.createUnreachable();
6510-
var arrayInstance = this.resolver.resolveClass(<ClassPrototype>arrayPrototype, [ elementType ]);
6511-
if (!arrayInstance) return module.createUnreachable();
6526+
var arrayInstance = assert(this.resolver.resolveClass(
6527+
<ClassPrototype>arrayPrototype,
6528+
[ elementType ],
6529+
makeMap<string,Type>()
6530+
));
65126531
var arrayType = arrayInstance.type;
65136532

65146533
// and compile an explicit instantiation
@@ -6660,13 +6679,13 @@ export class Compiler extends DiagnosticEmitter {
66606679
classInstance = this.resolver.resolveClass(
66616680
classPrototype,
66626681
classReference.typeArguments,
6663-
currentFunction.flow.contextualTypeArguments
6682+
makeMap<string,Type>(currentFunction.flow.contextualTypeArguments)
66646683
);
66656684
} else {
66666685
classInstance = this.resolver.resolveClassInclTypeArguments(
66676686
classPrototype,
66686687
typeArguments,
6669-
currentFunction.flow.contextualTypeArguments,
6688+
makeMap<string,Type>(currentFunction.flow.contextualTypeArguments),
66706689
expression
66716690
);
66726691
}

src/diagnosticMessages.generated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export enum DiagnosticCode {
121121
Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration = 2673,
122122
Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration = 2674,
123123
Namespace_0_has_no_exported_member_1 = 2694,
124+
Required_type_parameters_may_not_follow_optional_type_parameters = 2706,
124125
File_0_not_found = 6054,
125126
Numeric_separators_are_not_allowed_here = 6188,
126127
Multiple_consecutive_numeric_separators_are_not_permitted = 6189
@@ -243,6 +244,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
243244
case 2673: return "Constructor of class '{0}' is private and only accessible within the class declaration.";
244245
case 2674: return "Constructor of class '{0}' is protected and only accessible within the class declaration.";
245246
case 2694: return "Namespace '{0}' has no exported member '{1}'.";
247+
case 2706: return "Required type parameters may not follow optional type parameters.";
246248
case 6054: return "File '{0}' not found.";
247249
case 6188: return "Numeric separators are not allowed here.";
248250
case 6189: return "Multiple consecutive numeric separators are not permitted.";

src/diagnosticMessages.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"Constructor of class '{0}' is private and only accessible within the class declaration.": 2673,
116116
"Constructor of class '{0}' is protected and only accessible within the class declaration.": 2674,
117117
"Namespace '{0}' has no exported member '{1}'.": 2694,
118+
"Required type parameters may not follow optional type parameters.": 2706,
118119

119120
"File '{0}' not found.": 6054,
120121
"Numeric separators are not allowed here.": 6188,

0 commit comments

Comments
 (0)