Skip to content

Simplify compileCallExpression #2558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 26 additions & 122 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5990,11 +5990,8 @@ export class Compiler extends DiagnosticEmitter {
if (!target) return module.unreachable();
let thisExpression = this.resolver.currentThisExpression;

let signature: Signature | null;
let functionArg: ExpressionRef;
// handle direct call
switch (target.kind) {

// direct call: concrete function
case ElementKind.FunctionPrototype: {
let functionPrototype = <FunctionPrototype>target;
if (functionPrototype.hasDecorator(DecoratorFlags.Builtin)) {
Expand Down Expand Up @@ -6024,128 +6021,35 @@ export class Compiler extends DiagnosticEmitter {
constraints
);
}
}

// indirect call: first-class function (non-generic, can't be inlined)
case ElementKind.Local: {
let local = <Local>target;
signature = local.type.signatureReference;
if (signature) {
if (local.parent != flow.targetFunction) {
// TODO: closures
this.error(
DiagnosticCode.Not_implemented_0,
expression.range,
"Closures"
);
return module.unreachable();
}
if (local.is(CommonFlags.Inlined)) {
let inlinedValue = local.constantIntegerValue;
if (this.options.isWasm64) {
functionArg = module.i64(i64_low(inlinedValue), i64_high(inlinedValue));
} else {
assert(!i64_high(inlinedValue));
functionArg = module.i32(i64_low(inlinedValue));
}
} else {
functionArg = module.local_get(local.index, this.options.sizeTypeRef);
}
break;
}
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, local.type.toString()
);
return module.unreachable();
}
case ElementKind.Global: {
let global = <Global>target;
signature = global.type.signatureReference;
if (signature) {
functionArg = module.global_get(global.internalName, global.type.toRef());
break;
}
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, global.type.toString()
// handle indirect call
let functionArg = this.compileExpression(expression.expression, Type.auto);
let signature = this.currentType.getSignature();
if (signature) {
return this.compileCallIndirect(
signature,
functionArg,
expression.args,
expression,
0,
contextualType == Type.void
);
}
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, this.currentType.toString()
);
if (target.kind == ElementKind.PropertyPrototype) {
let getterPrototype = (<PropertyPrototype>target).getterPrototype;
if (getterPrototype) {
this.infoRelated(
DiagnosticCode.This_expression_is_not_callable_because_it_is_a_get_accessor_Did_you_mean_to_use_it_without,
expression.range, getterPrototype.identifierNode.range
);
return module.unreachable();
}
case ElementKind.PropertyPrototype: {
let propertyInstance = this.resolver.resolveProperty(<PropertyPrototype>target);
if (!propertyInstance) return module.unreachable();
target = propertyInstance;
// fall-through
}
case ElementKind.Property: {
let propertyInstance = <Property>target;
let getterInstance = propertyInstance.getterInstance;
let type = assert(this.resolver.getTypeOfElement(target));

if (!getterInstance) {
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, type.toString()
);
return module.unreachable();
}

let thisArg: ExpressionRef = 0;
if (propertyInstance.is(CommonFlags.Instance)) {
thisArg = this.compileExpression(
assert(thisExpression),
assert(getterInstance.signature.thisType),
Constraints.ConvImplicit | Constraints.IsThis
);
}
functionArg = this.compileCallDirect(getterInstance, [], expression.expression, thisArg);
signature = this.currentType.signatureReference;
if (!signature) {
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, this.currentType.toString()
);
return module.unreachable();
}
break;
}
case ElementKind.Class: {
let classInstance = <Class>target;
let typeArguments = classInstance.getTypeArgumentsTo(this.program.functionPrototype);
if (typeArguments && typeArguments.length > 0) {
let ftype = typeArguments[0];
signature = ftype.getSignature();
functionArg = this.compileExpression(expression.expression, ftype, Constraints.ConvImplicit);
break;
}
// fall-through
}

// not supported
default: {
let type = this.resolver.getTypeOfElement(target);
if (type) {
this.error(
DiagnosticCode.Type_0_has_no_call_signatures,
expression.range, type.toString()
);
} else {
this.error(
DiagnosticCode.Expression_cannot_be_represented_by_a_type,
expression.range
);
}
return module.unreachable();
}
}
return this.compileCallIndirect(
assert(signature), // FIXME: bootstrap can't see this yet
functionArg,
expression.args,
expression,
0,
contextualType == Type.void
);
return module.unreachable();
}

/** Compiles the given arguments like a call expression according to the specified context. */
Expand Down
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
"File '{0}' not found.": 6054,
"Numeric separators are not allowed here.": 6188,
"Multiple consecutive numeric separators are not permitted.": 6189,
"This expression is not callable because it is a 'get' accessor. Did you mean to use it without '()'?": 6234,
"'super' must be called before accessing 'this' in the constructor of a derived class.": 17009,
"'super' must be called before accessing a property of 'super' in the constructor of a derived class.": 17011
}
33 changes: 15 additions & 18 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2441,35 +2441,32 @@ export class Resolver extends DiagnosticEmitter {
) {
return this.resolveExpression(node.args[0], ctxFlow, ctxType, reportMode);
}
let instance = this.maybeInferCall(node, functionPrototype, ctxFlow, reportMode);
if (!instance) return null;
return instance.signature.returnType;
let functionInstance = this.maybeInferCall(node, functionPrototype, ctxFlow, reportMode);
if (!functionInstance) return null;
target = functionInstance;
// fall-through
}
case ElementKind.Function: {
return (<Function>target).signature.returnType;
}
case ElementKind.PropertyPrototype: {
let propertyInstance = this.resolveProperty(<PropertyPrototype>target, reportMode);
if (!propertyInstance) return null;
target = propertyInstance;
// fall-through
}
case ElementKind.Global:
case ElementKind.Local:
case ElementKind.Property: {
let varType = (<VariableLikeElement>target).type;
let varElement = this.getElementOfType(varType);
if (!varElement || varElement.kind != ElementKind.Class) {
break;
}
target = varElement;
default: {
if (!isTypedElement(target.kind)) break;
let targetElement = this.getElementOfType((<TypedElement>target).type);
if (!targetElement || targetElement.kind != ElementKind.Class) break;
target = targetElement;
// fall-through
}
case ElementKind.Class: {
let typeArguments = (<Class>target).getTypeArgumentsTo(this.program.functionPrototype);
if (typeArguments && typeArguments.length > 0) {
let ftype = typeArguments[0];
let signatureReference = assert(ftype.signatureReference);
return signatureReference.returnType;
}
break;
if (!(typeArguments && typeArguments.length)) break;
let signature = assert(typeArguments[0].getSignature());
return signature.returnType;
}
}
if (reportMode == ReportMode.Report) {
Expand Down
103 changes: 86 additions & 17 deletions tests/compiler/assert-nonnull.debug.wat
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,28 @@
i32.load $0 offset=4
)
(func $assert-nonnull/testFn (type $i32_=>_i32) (param $fn i32) (result i32)
(local $1 i32)
i32.const 0
global.set $~argumentsLength
local.get $fn
local.tee $1
if (result i32)
local.get $1
else
i32.const 32
i32.const 96
i32.const 35
i32.const 10
call $~lib/builtins/abort
unreachable
end
i32.load $0
call_indirect $0 (type $none_=>_i32)
)
(func $assert-nonnull/Foo#get:baz (type $i32_=>_i32) (param $this i32) (result i32)
local.get $this
i32.load $0 offset=4
)
(func $assert-nonnull/testObjFn (type $i32_=>_i32) (param $foo i32) (result i32)
i32.const 0
global.set $~argumentsLength
local.get $foo
call $assert-nonnull/Foo#get:baz
i32.load $0
call_indirect $0 (type $none_=>_i32)
)
(func $~stack_check (type $none_=>_none)
global.get $~lib/memory/__stack_pointer
global.get $~lib/memory/__data_end
Expand Down Expand Up @@ -538,6 +542,7 @@
(func $assert-nonnull/testRet (type $i32_=>_i32) (param $fn i32) (result i32)
(local $1 i32)
(local $2 i32)
(local $3 i32)
global.get $~lib/memory/__stack_pointer
i32.const 4
i32.sub
Expand All @@ -550,8 +555,55 @@
i32.const 0
global.set $~argumentsLength
local.get $fn
local.tee $1
if (result i32)
local.get $1
else
i32.const 32
i32.const 96
i32.const 44
i32.const 10
call $~lib/builtins/abort
unreachable
end
i32.load $0
call_indirect $0 (type $none_=>_i32)
local.tee $2
i32.store $0
local.get $2
if (result i32)
local.get $2
else
i32.const 32
i32.const 96
i32.const 44
i32.const 10
call $~lib/builtins/abort
unreachable
end
local.set $3
global.get $~lib/memory/__stack_pointer
i32.const 4
i32.add
global.set $~lib/memory/__stack_pointer
local.get $3
)
(func $assert-nonnull/testObjFn (type $i32_=>_i32) (param $foo i32) (result i32)
(local $1 i32)
(local $2 i32)
global.get $~lib/memory/__stack_pointer
i32.const 4
i32.sub
global.set $~lib/memory/__stack_pointer
call $~stack_check
global.get $~lib/memory/__stack_pointer
i32.const 0
i32.store $0
i32.const 0
global.set $~argumentsLength
global.get $~lib/memory/__stack_pointer
local.get $foo
call $assert-nonnull/Foo#get:baz
local.tee $1
i32.store $0
local.get $1
Expand All @@ -560,11 +612,13 @@
else
i32.const 32
i32.const 96
i32.const 44
i32.const 48
i32.const 10
call $~lib/builtins/abort
unreachable
end
i32.load $0
call_indirect $0 (type $none_=>_i32)
local.set $2
global.get $~lib/memory/__stack_pointer
i32.const 4
Expand All @@ -575,21 +629,21 @@
(func $assert-nonnull/testObjRet (type $i32_=>_i32) (param $foo i32) (result i32)
(local $1 i32)
(local $2 i32)
(local $3 i32)
global.get $~lib/memory/__stack_pointer
i32.const 4
i32.const 8
i32.sub
global.set $~lib/memory/__stack_pointer
call $~stack_check
global.get $~lib/memory/__stack_pointer
i32.const 0
i32.store $0
i64.const 0
i64.store $0
global.get $~lib/memory/__stack_pointer
i32.const 0
global.set $~argumentsLength
global.get $~lib/memory/__stack_pointer
local.get $foo
call $assert-nonnull/Foo#get:baz
i32.load $0
call_indirect $0 (type $none_=>_i32)
local.tee $1
i32.store $0
local.get $1
Expand All @@ -603,12 +657,27 @@
call $~lib/builtins/abort
unreachable
end
local.set $2
i32.load $0
call_indirect $0 (type $none_=>_i32)
local.tee $2
i32.store $0 offset=4
local.get $2
if (result i32)
local.get $2
else
i32.const 32
i32.const 96
i32.const 52
i32.const 10
call $~lib/builtins/abort
unreachable
end
local.set $3
global.get $~lib/memory/__stack_pointer
i32.const 4
i32.const 8
i32.add
global.set $~lib/memory/__stack_pointer
local.get $2
local.get $3
)
(func $export:assert-nonnull/testVar (type $i32_=>_i32) (param $0 i32) (result i32)
(local $1 i32)
Expand Down
Loading