Basic type inference

This commit is contained in:
dcodeIO 2017-12-23 13:48:04 +01:00
parent ad298c7bea
commit ca9c79185b
9 changed files with 221 additions and 48 deletions

View File

@ -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");

View File

@ -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>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(<TypeNode>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>type, name);
if (declaration.initializer)
initializers.push(this.compileAssignment(declaration.name, <Expression>declaration.initializer, Type.void));
}
type = this.program.resolveType(<TypeNode>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, <Expression>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((<EnumValue>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;

View File

@ -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. */

View File

@ -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)
)
)
)

View File

@ -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)
)
)

View File

@ -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();

View File

@ -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]
;)

View File

@ -0,0 +1,4 @@
var; while for let; a from "./other";
do {
;
} while (false);

View File

@ -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