diff --git a/src/ast.ts b/src/ast.ts index f4c08497..f4528850 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1059,8 +1059,7 @@ export class Source extends Node { for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) { const statement: Statement = this.statements[i]; statement.serialize(sb); - const last: string = sb[sb.length - 1]; - if (last.charCodeAt(last.length - 1) == CharCode.CLOSEBRACE) + if (builderEndsWith(sb, CharCode.CLOSEBRACE)) sb.push("\n"); else sb.push(";\n"); diff --git a/src/compiler.ts b/src/compiler.ts index b3ff1901..9cb2cb28 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -324,48 +324,48 @@ export class Compiler extends DiagnosticEmitter { compileGlobal(global: Global): bool { if (global.isCompiled) return true; - if (global.isBuiltIn) - if (compileBuiltinGetGlobal(this, global)) - return true; + if (global.isBuiltIn && compileBuiltinGetGlobal(this, global)) + return true; const declaration: VariableLikeDeclarationStatement | null = global.declaration; - let type: Type | null = global.type; - if (!type) { - if (!declaration) - throw new Error("unexpected missing declaration"); - if (!declaration.type) { // TODO: infer type - this.error(DiagnosticCode.Type_expected, declaration.name.range); + if (!global.type) { + if (declaration && declaration.type) { + global.type = this.program.resolveType(declaration.type); // reports + if (!global.type) + return false; + } else if (declaration && declaration.initializer) { + global.type = this.determineExpressionType(declaration.initializer); + if (!global.type) + return false; + } else if (declaration) { + this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd); return false; - } - type = this.program.resolveType(declaration.type); // reports - if (!type) - return false; - global.type = type; + } else + throw new Error("unable to infer type"); } if (this.module.noEmit) return true; - const nativeType: NativeType = typeToNativeType(type); + const nativeType: NativeType = typeToNativeType(global.type); let initializer: ExpressionRef; let initializeInStart: bool = false; if (global.hasConstantValue) { - assert(type != null); - if (type.isLongInteger) + if (global.type.isLongInteger) initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0); - else if (type.kind == TypeKind.F32) + else if (global.type.kind == TypeKind.F32) initializer = this.module.createF32(global.constantFloatValue); - else if (type.kind == TypeKind.F64) + else if (global.type.kind == TypeKind.F64) initializer = this.module.createF64(global.constantFloatValue); - else if (type.isSmallInteger) { - if (type.isSignedInteger) { - const shift: i32 = type.smallIntegerShift; + else if (global.type.isSmallInteger) { + if (global.type.isSignedInteger) { + const shift: i32 = global.type.smallIntegerShift; initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0); } else - initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() & type.smallIntegerMask: 0); + initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() & global.type.smallIntegerMask: 0); } else initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0); } else if (declaration) { if (declaration.initializer) { - initializer = this.compileExpression(declaration.initializer, type); + initializer = this.compileExpression(declaration.initializer, global.type); if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { if (!global.isMutable) { initializer = this.precomputeExpressionRef(initializer); @@ -377,13 +377,13 @@ export class Compiler extends DiagnosticEmitter { initializeInStart = true; } } else - initializer = typeToNativeZero(this.module, type); + initializer = typeToNativeZero(this.module, global.type); } else throw new Error("unexpected missing declaration or constant value"); const internalName: string = global.internalName; if (initializeInStart) { - this.module.addGlobal(internalName, nativeType, true, typeToNativeZero(this.module, type)); + this.module.addGlobal(internalName, nativeType, true, typeToNativeZero(this.module, global.type)); this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer)); } else { this.module.addGlobal(internalName, nativeType, global.isMutable, initializer); @@ -717,7 +717,7 @@ export class Compiler extends DiagnosticEmitter { // types // TODO: try to get rid of this - determineExpressionType(expression: Expression, contextualType: Type): Type { + determineExpressionType(expression: Expression, contextualType: Type = Type.void): Type { const previousType: Type = this.currentType; const previousNoEmit: bool = this.module.noEmit; this.module.noEmit = true; @@ -971,19 +971,26 @@ export class Compiler extends DiagnosticEmitter { const initializers: ExpressionRef[] = new Array(); for (let i: i32 = 0, k = declarations.length; i < k; ++i) { const declaration: VariableDeclaration = declarations[i]; + const name: string = declaration.name.name; + let type: Type | null = null; if (declaration.type) { - const name: string = declaration.name.name; - const type: Type | null = this.program.resolveType(declaration.type, this.currentFunction.contextualTypeArguments, true); // reports - if (type) { - if (this.currentFunction.locals.has(name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, name); // recoverable - else - this.currentFunction.addLocal(type, name); - if (declaration.initializer) - initializers.push(this.compileAssignment(declaration.name, declaration.initializer, Type.void)); - } + type = this.program.resolveType(declaration.type, this.currentFunction.contextualTypeArguments, true); // reports + if (!type) + continue; + } else if (declaration.initializer) { + type = this.determineExpressionType(declaration.initializer); // reports + if (!type) + continue; } else { - this.error(DiagnosticCode.Type_expected, declaration.name.range); + this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd); + continue; + } + if (this.currentFunction.locals.has(name)) + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, name); // recoverable + else { + this.currentFunction.addLocal(type, name); + if (declaration.initializer) + initializers.push(this.compileAssignment(declaration.name, declaration.initializer, Type.void)); } } return initializers.length ? this.module.createBlock(null, initializers, NativeType.None) : this.module.createNop(); @@ -1100,11 +1107,6 @@ export class Compiler extends DiagnosticEmitter { if (conversionKind == ConversionKind.NONE) return expr; - if (!fromType) { - _BinaryenExpressionPrint(expr); - throw new Error("WHAT"); - } - // void to any if (fromType.kind == TypeKind.VOID) { this.error(DiagnosticCode.Operation_not_supported, reportNode.range); @@ -1843,6 +1845,10 @@ export class Compiler extends DiagnosticEmitter { return this.module.createI64(intValue.lo, intValue.hi); if (contextualType.isSmallInteger) return this.module.createI32(intValue.toI32()); + if (contextualType == Type.void && !intValue.fitsInI32) { + this.currentType = Type.i64; + return this.module.createI64(intValue.lo, intValue.hi); + } this.currentType = contextualType.isSignedInteger ? Type.i32 : Type.u32; return this.module.createI32(intValue.toI32()); } @@ -1942,7 +1948,7 @@ export class Compiler extends DiagnosticEmitter { : this.module.createGetGlobal((element).internalName, NativeType.I32); } } else { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName); + this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName); return this.module.createUnreachable(); } break; diff --git a/src/types.ts b/src/types.ts index 94b2f034..935b7943 100644 --- a/src/types.ts +++ b/src/types.ts @@ -155,8 +155,6 @@ export class Type { static readonly f64: Type = new Type(TypeKind.F64, 64); /** No return type. */ static readonly void: Type = new Type(TypeKind.VOID, 0); - /** Special type used in type inference. Alias of {@link Type.void}. */ - static readonly auto: Type = Type.void; } /** Converts an array of types to its combined string representation. Usually type arguments. */ diff --git a/tests/compiler/infer-type.optimized-inlined.wast b/tests/compiler/infer-type.optimized-inlined.wast new file mode 100644 index 00000000..4ba61d2e --- /dev/null +++ b/tests/compiler/infer-type.optimized-inlined.wast @@ -0,0 +1,11 @@ +(module + (type $v (func)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $start (; 0 ;) (type $v) + (block $__inlined_func$infer-type/locals + (nop) + ) + ) +) diff --git a/tests/compiler/infer-type.optimized.wast b/tests/compiler/infer-type.optimized.wast new file mode 100644 index 00000000..a0345d3a --- /dev/null +++ b/tests/compiler/infer-type.optimized.wast @@ -0,0 +1,12 @@ +(module + (type $v (func)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $infer-type/locals (; 0 ;) (type $v) + (nop) + ) + (func $start (; 1 ;) (type $v) + (call $infer-type/locals) + ) +) diff --git a/tests/compiler/infer-type.ts b/tests/compiler/infer-type.ts new file mode 100644 index 00000000..d1c86c57 --- /dev/null +++ b/tests/compiler/infer-type.ts @@ -0,0 +1,18 @@ +const i = 10; +i; + +const I = 0x100000000; +I; + +const F = 1.5; +F; + +function locals(): void { + let li = 10; + let lI = 0x100000000; + let lF = 1.5; + let ai = i; + let aI = I; + let aF = F; +} +locals(); diff --git a/tests/compiler/infer-type.wast b/tests/compiler/infer-type.wast new file mode 100644 index 00000000..24a4d12a --- /dev/null +++ b/tests/compiler/infer-type.wast @@ -0,0 +1,113 @@ +(module + (type $v (func)) + (global $infer-type/i i32 (i32.const 10)) + (global $infer-type/I i64 (i64.const 4294967296)) + (global $infer-type/F f64 (f64.const 1.5)) + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $infer-type/locals (; 0 ;) (type $v) + (local $0 i32) + (local $1 i64) + (local $2 f64) + (local $3 i32) + (local $4 i64) + (local $5 f64) + (block + (set_local $0 + (i32.const 10) + ) + ) + (block + (set_local $1 + (i64.const 4294967296) + ) + ) + (block + (set_local $2 + (f64.const 1.5) + ) + ) + (block + (set_local $3 + (i32.const 10) + ) + ) + (block + (set_local $4 + (i64.const 4294967296) + ) + ) + (block + (set_local $5 + (f64.const 1.5) + ) + ) + ) + (func $start (; 1 ;) (type $v) + (drop + (i32.const 10) + ) + (drop + (i64.const 4294967296) + ) + (drop + (f64.const 1.5) + ) + (call $infer-type/locals) + ) +) +(; +[program.elements] + NaN + Infinity + isNaN + isFinite + clz + ctz + popcnt + rotl + rotr + abs + max + min + ceil + floor + copysign + nearest + reinterpret + sqrt + trunc + load + store + sizeof + select + unreachable + current_memory + grow_memory + parseInt + parseFloat + changetype + assert + i8 + i16 + i32 + i64 + u8 + u16 + u32 + u64 + bool + f32 + f64 + isize + usize + HEAP_BASE + infer-type/i + infer-type/I + infer-type/F + infer-type/locals +[program.exports] + +;) diff --git a/tests/parser/continue-on-error.ts b/tests/parser/continue-on-error.ts new file mode 100644 index 00000000..a6e8aab1 --- /dev/null +++ b/tests/parser/continue-on-error.ts @@ -0,0 +1,4 @@ +var; while for let; a from "./other"; +do { + ; +} while (false); diff --git a/tests/parser/continue-on-error.ts.fixture.ts b/tests/parser/continue-on-error.ts.fixture.ts new file mode 100644 index 00000000..6ade9f5d --- /dev/null +++ b/tests/parser/continue-on-error.ts.fixture.ts @@ -0,0 +1,12 @@ +; +; +a; +from; +"./other"; +do { +; +} while (false); +// ERROR 1003: "Identifier expected." in continue-on-error.ts @ 0,3 +// ERROR 1005: "'(' expected." in continue-on-error.ts @ 5,10 +// ERROR 1005: "'(' expected." in continue-on-error.ts @ 11,14 +// ERROR 1003: "Identifier expected." in continue-on-error.ts @ 15,18