Skip inlining and emit a warning when trying to inline a function into itself

This commit is contained in:
dcodeIO 2018-07-14 18:01:19 +02:00
parent 3b0fd9aac2
commit 4b8500355a
12 changed files with 52 additions and 9 deletions

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -54,7 +54,6 @@ import {
import {
ReportMode
} from "./resolver";
import { CommonFlags } from "./common";
/** Compiles a call to a built-in function. */
export function compileCall(
@ -73,7 +72,7 @@ export function compileCall(
ret: ExpressionRef;
// NOTE that some implementations below make use of the select expression where straight-forward.
// whether worth or not should probably be tested once/ it's known if/how embedders handle it.
// whether worth or not should probably be tested once it's known if/how embedders handle it.
// search: createSelect
switch (prototype.internalName) {

View File

@ -265,6 +265,8 @@ export class Compiler extends DiagnosticEmitter {
currentFunction: Function;
/** Current outer function in compilation, if compiling a function expression. */
currentOuterFunction: Function | null = null;
/** Current inline function in compilation. */
currentInlineFunction: Function | null = null;
/** Current enum in compilation. */
currentEnum: Enum | null = null;
/** Current type in compilation. */
@ -5257,7 +5259,21 @@ export class Compiler extends DiagnosticEmitter {
// Inline if explicitly requested
if (inline) {
assert(!instance.is(CommonFlags.TRAMPOLINE)); // doesn't make sense
return this.compileCallInlineUnchecked(instance, argumentExpressions, reportNode, thisArg);
if (instance === this.currentInlineFunction) {
// skip inlining when trying to inline a function into itself and print a warning when
// instead compiling the function the normal way.
if (instance === this.currentFunction) {
this.warning(
DiagnosticCode.Function_0_cannot_be_inlined_into_itself,
reportNode.range, instance.internalName
);
}
} else {
this.currentInlineFunction = instance;
let ret = this.compileCallInlineUnchecked(instance, argumentExpressions, reportNode, thisArg);
this.currentInlineFunction = null;
return ret;
}
}
// Otherwise compile to just a call

View File

@ -26,6 +26,7 @@ export enum DiagnosticCode {
An_allocator_must_be_declared_to_allocate_memory_Try_importing_allocator_arena_or_allocator_tlsf = 214,
Optional_parameter_must_have_an_initializer = 215,
Constructor_of_class_0_must_not_require_any_arguments = 216,
Function_0_cannot_be_inlined_into_itself = 217,
Unterminated_string_literal = 1002,
Identifier_expected = 1003,
_0_expected = 1005,
@ -142,6 +143,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 214: return "An allocator must be declared to allocate memory. Try importing allocator/arena or allocator/tlsf.";
case 215: return "Optional parameter must have an initializer.";
case 216: return "Constructor of class '{0}' must not require any arguments.";
case 217: return "Function '{0}' cannot be inlined into itself.";
case 1002: return "Unterminated string literal.";
case 1003: return "Identifier expected.";
case 1005: return "'{0}' expected.";

View File

@ -18,6 +18,7 @@
"An allocator must be declared to allocate memory. Try importing allocator/arena or allocator/tlsf.": 214,
"Optional parameter must have an initializer.": 215,
"Constructor of class '{0}' must not require any arguments.": 216,
"Function '{0}' cannot be inlined into itself.": 217,
"Unterminated string literal.": 1002,
"Identifier expected.": 1003,

View File

@ -304,9 +304,9 @@ declare function isReference<T>(value?: any): value is object | string;
declare function isString<T>(value?: any): value is string | String;
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
declare function isArray<T>(value?: any): value is Array<any>;
/** Tests if the specified expression resolves to a defined element. */
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
declare function isDefined(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. */
/** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */
declare function isConstant(expression: any): bool;
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?

View File

@ -0,0 +1,9 @@
(module
(type $v (func))
(memory $0 0)
(export "foo" (func $inlining-recursive/foo))
(export "memory" (memory $0))
(func $inlining-recursive/foo (; 0 ;) (type $v)
(call $inlining-recursive/foo)
)
)

View File

@ -0,0 +1,4 @@
@inline
export function foo(): void {
foo();
}

View File

@ -0,0 +1,12 @@
(module
(type $v (func))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "foo" (func $inlining-recursive/foo))
(export "memory" (memory $0))
(func $inlining-recursive/foo (; 0 ;) (type $v)
(block $inlining-recursive/foo|inlined.0
(call $inlining-recursive/foo)
)
)
)