Skip to content

Commit

Permalink
fix: Simplify compileCallExpression (#2558)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodeIO authored Nov 12, 2022
1 parent a150a49 commit a71f649
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 208 deletions.
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

0 comments on commit a71f649

Please sign in to comment.