Skip to content

Commit c7740fe

Browse files
authored
Share call infer logic between resolver and compiler (AssemblyScript#915)
1 parent 58dbff7 commit c7740fe

File tree

2 files changed

+111
-128
lines changed

2 files changed

+111
-128
lines changed

src/compiler.ts

Lines changed: 4 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5822,110 +5822,22 @@ export class Compiler extends DiagnosticEmitter {
58225822
// direct call: concrete function
58235823
case ElementKind.FUNCTION_PROTOTYPE: {
58245824
let prototype = <FunctionPrototype>target;
5825-
let typeArguments = expression.typeArguments;
58265825

58275826
// builtins handle present respectively omitted type arguments on their own
58285827
if (prototype.hasDecorator(DecoratorFlags.BUILTIN)) {
58295828
return this.compileCallExpressionBuiltin(prototype, expression, contextualType);
58305829
}
58315830

5832-
let instance: Function | null = null;
58335831
let thisExpression = this.resolver.currentThisExpression;
5834-
5835-
// resolve generic call if type arguments have been provided
5836-
if (typeArguments) {
5837-
if (!prototype.is(CommonFlags.GENERIC)) {
5838-
this.error(
5839-
DiagnosticCode.Type_0_is_not_generic,
5840-
expression.expression.range, prototype.internalName
5841-
);
5842-
return module.unreachable();
5843-
}
5844-
instance = this.resolver.resolveFunctionInclTypeArguments(
5845-
prototype,
5846-
typeArguments,
5847-
flow.actualFunction.parent, // relative to caller
5848-
makeMap<string,Type>(flow.contextualTypeArguments),
5849-
expression
5850-
);
5851-
5852-
// infer generic call if type arguments have been omitted
5853-
} else if (prototype.is(CommonFlags.GENERIC)) {
5854-
let contextualTypeArguments = makeMap<string,Type>(flow.contextualTypeArguments);
5855-
5856-
// fill up contextual types with auto for each generic component
5857-
let typeParameterNodes = assert(prototype.typeParameterNodes);
5858-
let numTypeParameters = typeParameterNodes.length;
5859-
let typeParameterNames = new Set<string>();
5860-
for (let i = 0; i < numTypeParameters; ++i) {
5861-
let name = typeParameterNodes[i].name.text;
5862-
contextualTypeArguments.set(name, Type.auto);
5863-
typeParameterNames.add(name);
5864-
}
5865-
5866-
let parameterNodes = prototype.functionTypeNode.parameters;
5867-
let numParameters = parameterNodes.length;
5868-
let argumentNodes = expression.arguments;
5869-
let numArguments = argumentNodes.length;
5870-
5871-
// infer types with generic components while updating contextual types
5872-
for (let i = 0; i < numParameters; ++i) {
5873-
let argumentExpression = i < numArguments ? argumentNodes[i] : parameterNodes[i].initializer;
5874-
if (!argumentExpression) { // missing initializer -> too few arguments
5875-
this.error(
5876-
DiagnosticCode.Expected_0_arguments_but_got_1,
5877-
expression.range, numParameters.toString(10), numArguments.toString(10)
5878-
);
5879-
return module.unreachable();
5880-
}
5881-
let typeNode = parameterNodes[i].type;
5882-
if (typeNode.hasGenericComponent(typeParameterNodes)) {
5883-
this.resolver.inferGenericType(typeNode, argumentExpression, flow, contextualTypeArguments, typeParameterNames);
5884-
}
5885-
}
5886-
5887-
// apply concrete types to the generic function signature
5888-
let resolvedTypeArguments = new Array<Type>(numTypeParameters);
5889-
for (let i = 0; i < numTypeParameters; ++i) {
5890-
let name = typeParameterNodes[i].name.text;
5891-
if (contextualTypeArguments.has(name)) {
5892-
let inferredType = contextualTypeArguments.get(name)!;
5893-
if (inferredType != Type.auto) {
5894-
resolvedTypeArguments[i] = inferredType;
5895-
continue;
5896-
}
5897-
}
5898-
// unused template, e.g. `function test<T>(): void {...}` called as `test()`
5899-
// invalid because the type is effectively unknown inside the function body
5900-
this.error(
5901-
DiagnosticCode.Type_argument_expected,
5902-
expression.expression.range.atEnd
5903-
);
5904-
return this.module.unreachable();
5905-
}
5906-
instance = this.resolver.resolveFunction(
5907-
prototype,
5908-
resolvedTypeArguments,
5909-
makeMap<string,Type>(flow.contextualTypeArguments)
5910-
);
5911-
5912-
// otherwise resolve the non-generic call as usual
5913-
} else {
5914-
instance = this.resolver.resolveFunction(prototype, null);
5915-
}
5832+
let instance = this.resolver.maybeInferCall(expression, prototype, flow);
59165833
if (!instance) return this.module.unreachable();
5917-
5918-
// compile 'this' expression if an instance method
5919-
let thisExpr: ExpressionRef = 0;
5920-
if (instance.is(CommonFlags.INSTANCE)) {
5921-
thisExpr = this.compileExpression(assert(thisExpression), this.options.usizeType);
5922-
}
5923-
59245834
return this.compileCallDirect(
59255835
instance,
59265836
expression.arguments,
59275837
expression,
5928-
thisExpr,
5838+
instance.is(CommonFlags.INSTANCE)
5839+
? this.compileExpression(assert(thisExpression), this.options.usizeType)
5840+
: 0,
59295841
constraints
59305842
);
59315843
}

src/resolver.ts

Lines changed: 107 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,13 @@ export class Resolver extends DiagnosticEmitter {
188188
}
189189
}
190190
if (node.isNullable) {
191-
if (!type.is(TypeFlags.REFERENCE)) {
192-
if (reportMode == ReportMode.REPORT) {
193-
this.error(
194-
DiagnosticCode.Basic_type_0_cannot_be_nullable,
195-
node.range, type.toString()
196-
);
197-
}
191+
if (type.is(TypeFlags.REFERENCE)) return type.asNullable();
192+
if (reportMode == ReportMode.REPORT) {
193+
this.error(
194+
DiagnosticCode.Basic_type_0_cannot_be_nullable,
195+
node.range, type.toString()
196+
);
198197
}
199-
return type.asNullable();
200198
}
201199
return type;
202200
}
@@ -692,21 +690,105 @@ export class Resolver extends DiagnosticEmitter {
692690
return typeArguments;
693691
}
694692

695-
/** Infers the generic type(s) of an argument expression and updates `ctxTypes`. */
696-
inferGenericType(
697-
/** The generic type being inferred. */
698-
typeNode: TypeNode,
699-
/** The respective argument expression. */
700-
exprNode: Expression,
701-
/** Contextual flow. */
693+
/** Resolves respectively infers the concrete instance of a function by call context. */
694+
maybeInferCall(
695+
node: CallExpression,
696+
prototype: FunctionPrototype,
702697
ctxFlow: Flow,
703-
/** Contextual types, i.e. `T`, with unknown types initialized to `auto`. */
704-
ctxTypes: Map<string,Type>,
705-
/** The names of the type parameters being inferred. */
706-
typeParameterNames: Set<string>
707-
): void {
708-
var type = this.resolveExpression(exprNode, ctxFlow, Type.auto, ReportMode.SWALLOW);
709-
if (type) this.propagateInferredGenericTypes(typeNode, type, ctxFlow, ctxTypes, typeParameterNames);
698+
reportMode: ReportMode = ReportMode.REPORT
699+
): Function | null {
700+
var typeArguments = node.typeArguments;
701+
702+
// resolve generic call if type arguments have been provided
703+
if (typeArguments) {
704+
if (!prototype.is(CommonFlags.GENERIC)) {
705+
if (reportMode == ReportMode.REPORT) {
706+
this.error(
707+
DiagnosticCode.Type_0_is_not_generic,
708+
node.expression.range, prototype.internalName
709+
);
710+
}
711+
return null;
712+
}
713+
return this.resolveFunctionInclTypeArguments(
714+
prototype,
715+
node.typeArguments,
716+
ctxFlow.actualFunction,
717+
makeMap(ctxFlow.contextualTypeArguments), // don't inherit
718+
node,
719+
reportMode
720+
);
721+
}
722+
723+
// infer generic call if type arguments have been omitted
724+
if (prototype.is(CommonFlags.GENERIC)) {
725+
let contextualTypeArguments = makeMap<string,Type>(ctxFlow.contextualTypeArguments);
726+
727+
// fill up contextual types with auto for each generic component
728+
let typeParameterNodes = assert(prototype.typeParameterNodes);
729+
let numTypeParameters = typeParameterNodes.length;
730+
let typeParameterNames = new Set<string>();
731+
for (let i = 0; i < numTypeParameters; ++i) {
732+
let name = typeParameterNodes[i].name.text;
733+
contextualTypeArguments.set(name, Type.auto);
734+
typeParameterNames.add(name);
735+
}
736+
737+
let parameterNodes = prototype.functionTypeNode.parameters;
738+
let numParameters = parameterNodes.length;
739+
let argumentNodes = node.arguments;
740+
let numArguments = argumentNodes.length;
741+
742+
// infer types with generic components while updating contextual types
743+
for (let i = 0; i < numParameters; ++i) {
744+
let argumentExpression = i < numArguments ? argumentNodes[i] : parameterNodes[i].initializer;
745+
if (!argumentExpression) { // missing initializer -> too few arguments
746+
if (reportMode == ReportMode.REPORT) {
747+
this.error(
748+
DiagnosticCode.Expected_0_arguments_but_got_1,
749+
node.range, numParameters.toString(10), numArguments.toString(10)
750+
);
751+
}
752+
return null;
753+
}
754+
let typeNode = parameterNodes[i].type;
755+
if (typeNode.hasGenericComponent(typeParameterNodes)) {
756+
let type = this.resolveExpression(argumentExpression, ctxFlow, Type.auto, ReportMode.SWALLOW);
757+
if (type) this.propagateInferredGenericTypes(typeNode, type, ctxFlow, contextualTypeArguments, typeParameterNames);
758+
}
759+
}
760+
761+
// apply concrete types to the generic function signature
762+
let resolvedTypeArguments = new Array<Type>(numTypeParameters);
763+
for (let i = 0; i < numTypeParameters; ++i) {
764+
let name = typeParameterNodes[i].name.text;
765+
if (contextualTypeArguments.has(name)) {
766+
let inferredType = contextualTypeArguments.get(name)!;
767+
if (inferredType != Type.auto) {
768+
resolvedTypeArguments[i] = inferredType;
769+
continue;
770+
}
771+
}
772+
// unused template, e.g. `function test<T>(): void {...}` called as `test()`
773+
// invalid because the type is effectively unknown inside the function body
774+
if (reportMode == ReportMode.REPORT) {
775+
this.error(
776+
DiagnosticCode.Type_argument_expected,
777+
node.expression.range.atEnd
778+
);
779+
}
780+
return null;
781+
}
782+
return this.resolveFunction(
783+
prototype,
784+
resolvedTypeArguments,
785+
makeMap<string,Type>(ctxFlow.contextualTypeArguments),
786+
reportMode
787+
);
788+
}
789+
790+
// otherwise resolve the non-generic call as usual
791+
return this.resolveFunction(prototype, null, makeMap<string,Type>(), reportMode);
710792
}
711793

712794
/** Updates contextual types with a possibly encapsulated inferred type. */
@@ -2185,31 +2267,20 @@ export class Resolver extends DiagnosticEmitter {
21852267
reportMode
21862268
);
21872269
if (!target) return null;
2188-
21892270
switch (target.kind) {
21902271
case ElementKind.FUNCTION_PROTOTYPE: {
2191-
// `unchecked(expr: *): *` is special
2272+
// `unchecked` behaves like parenthesized
21922273
if (
21932274
(<FunctionPrototype>target).internalName == BuiltinSymbols.unchecked &&
21942275
node.arguments.length > 0
21952276
) {
21962277
return this.resolveExpression(node.arguments[0], ctxFlow, ctxType, reportMode);
21972278
}
2198-
// otherwise resolve normally
2199-
let instance = this.resolveFunctionInclTypeArguments(
2200-
<FunctionPrototype>target,
2201-
node.typeArguments,
2202-
ctxFlow.actualFunction,
2203-
makeMap(ctxFlow.contextualTypeArguments), // don't inherit
2204-
node,
2205-
reportMode
2206-
);
2279+
let instance = this.maybeInferCall(node, <FunctionPrototype>target, ctxFlow, reportMode);
22072280
if (!instance) return null;
22082281
return instance.signature.returnType;
22092282
}
2210-
case ElementKind.FUNCTION_TARGET: {
2211-
return (<FunctionTarget>target).signature.returnType;
2212-
}
2283+
case ElementKind.FUNCTION_TARGET: return (<FunctionTarget>target).signature.returnType;
22132284
}
22142285
if (reportMode == ReportMode.REPORT) {
22152286
this.error(

0 commit comments

Comments
 (0)