Skip to content

Commit 1208c15

Browse files
Support rest params in function calls
1 parent 9a7a6e0 commit 1208c15

File tree

3 files changed

+53
-14
lines changed

3 files changed

+53
-14
lines changed

src/compiler.ts

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ import {
182182
findDecorator,
183183
isTypeOmitted,
184184
Source,
185-
TypeDeclaration
185+
TypeDeclaration,
186+
ParameterKind
186187
} from "./ast";
187188

188189
import {
@@ -6171,16 +6172,7 @@ export class Compiler extends DiagnosticEmitter {
61716172
return false;
61726173
}
61736174

6174-
// not yet implemented (TODO: maybe some sort of an unmanaged/lightweight array?)
61756175
let hasRest = signature.hasRest;
6176-
if (hasRest) {
6177-
this.error(
6178-
DiagnosticCode.Not_implemented_0,
6179-
reportNode.range, "Rest parameters"
6180-
);
6181-
return false;
6182-
}
6183-
61846176
let minimum = signature.requiredParameters;
61856177
let maximum = signature.parameterTypes.length;
61866178

@@ -6225,6 +6217,37 @@ export class Compiler extends DiagnosticEmitter {
62256217
}
62266218
}
62276219

6220+
private adjustArgumentsForRestParams(
6221+
argumentExpressions: Expression[],
6222+
signature: Signature,
6223+
reportNode: Node
6224+
) : Expression[] {
6225+
6226+
// if no rest args, return the original args
6227+
if (!signature.hasRest) {
6228+
return argumentExpressions;
6229+
}
6230+
6231+
// if there are fewer args than params, then the rest args were not provided
6232+
// so return the original args
6233+
const numArguments = argumentExpressions.length;
6234+
const numParams = signature.parameterTypes.length;
6235+
if (numArguments < numParams) {
6236+
return argumentExpressions;
6237+
}
6238+
6239+
// make an array literal expression from the rest args
6240+
let elements = argumentExpressions.slice(numParams - 1);
6241+
let range = new Range(elements[0].range.start, elements[elements.length - 1].range.end);
6242+
range.source = reportNode.range.source;
6243+
let arrExpr = new ArrayLiteralExpression(elements, range);
6244+
6245+
// return the original args, but replace the rest args with the array
6246+
const exprs = argumentExpressions.slice(0, numParams - 1);
6247+
exprs.push(arrExpr);
6248+
return exprs;
6249+
}
6250+
62286251
/** Compiles a direct call to a concrete function. */
62296252
compileCallDirect(
62306253
instance: Function,
@@ -6246,6 +6269,9 @@ export class Compiler extends DiagnosticEmitter {
62466269
}
62476270
if (instance.hasDecorator(DecoratorFlags.Unsafe)) this.checkUnsafe(reportNode);
62486271

6272+
argumentExpressions = this.adjustArgumentsForRestParams(argumentExpressions, signature, reportNode);
6273+
numArguments = argumentExpressions.length;
6274+
62496275
// handle call on `this` in constructors
62506276
let sourceFunction = this.currentFlow.sourceFunction;
62516277
if (sourceFunction.is(CommonFlags.Constructor) && reportNode.isAccessOnThis) {
@@ -6477,7 +6503,11 @@ export class Compiler extends DiagnosticEmitter {
64776503
let declaration = originalParameterDeclarations[minArguments + i];
64786504
let initializer = declaration.initializer;
64796505
let initExpr: ExpressionRef;
6480-
if (initializer) {
6506+
if (declaration.parameterKind === ParameterKind.Rest) {
6507+
const arrExpr = new ArrayLiteralExpression([], declaration.range.atEnd);
6508+
initExpr = this.compileArrayLiteral(arrExpr, type, Constraints.ConvExplicit);
6509+
initExpr = module.local_set(operandIndex, initExpr, type.isManaged);
6510+
} else if (initializer) {
64816511
initExpr = this.compileExpression(
64826512
initializer,
64836513
type,
@@ -6863,6 +6893,9 @@ export class Compiler extends DiagnosticEmitter {
68636893
return this.module.unreachable();
68646894
}
68656895

6896+
argumentExpressions = this.adjustArgumentsForRestParams(argumentExpressions, signature, reportNode);
6897+
numArguments = argumentExpressions.length;
6898+
68666899
let numArgumentsInclThis = thisArg ? numArguments + 1 : numArguments;
68676900
let operands = new Array<ExpressionRef>(numArgumentsInclThis);
68686901
let index = 0;

src/resolver.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,9 @@ export class Resolver extends DiagnosticEmitter {
812812
return null;
813813
}
814814
let typeNode = parameterNodes[i].type;
815+
if (parameterNodes[i].parameterKind == ParameterKind.Rest) {
816+
typeNode = (<NamedTypeNode> typeNode).typeArguments![0];
817+
}
815818
if (typeNode.hasGenericComponent(typeParameterNodes)) {
816819
let type = this.resolveExpression(argumentExpression, ctxFlow, Type.auto, ReportMode.Swallow);
817820
if (type) {
@@ -2921,10 +2924,13 @@ export class Resolver extends DiagnosticEmitter {
29212924
let numSignatureParameters = signatureParameters.length;
29222925
let parameterTypes = new Array<Type>(numSignatureParameters);
29232926
let requiredParameters = 0;
2927+
let hasRest = false;
29242928
for (let i = 0; i < numSignatureParameters; ++i) {
29252929
let parameterDeclaration = signatureParameters[i];
29262930
if (parameterDeclaration.parameterKind == ParameterKind.Default) {
29272931
requiredParameters = i + 1;
2932+
} else if (parameterDeclaration.parameterKind == ParameterKind.Rest) {
2933+
hasRest = true;
29282934
}
29292935
let typeNode = parameterDeclaration.type;
29302936
if (isTypeOmitted(typeNode)) {
@@ -2984,7 +2990,7 @@ export class Resolver extends DiagnosticEmitter {
29842990
returnType = type;
29852991
}
29862992

2987-
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters);
2993+
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);
29882994

29892995
let nameInclTypeParameters = prototype.name;
29902996
if (instanceKey.length) nameInclTypeParameters += `<${instanceKey}>`;

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ export class Signature {
10751075
}
10761076

10771077
// check rest parameter
1078-
if (this.hasRest != target.hasRest) return false; // TODO
1078+
if (this.hasRest != target.hasRest) return false;
10791079

10801080
// check return type (covariant)
10811081
let thisReturnType = this.returnType;
@@ -1087,7 +1087,7 @@ export class Signature {
10871087
let thisParameterTypes = this.parameterTypes;
10881088
let targetParameterTypes = target.parameterTypes;
10891089
let numParameters = thisParameterTypes.length;
1090-
if (numParameters != targetParameterTypes.length) return false; // TODO
1090+
if (numParameters != targetParameterTypes.length) return false;
10911091

10921092
for (let i = 0; i < numParameters; ++i) {
10931093
let thisParameterType = unchecked(thisParameterTypes[i]);

0 commit comments

Comments
 (0)