mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 23:12:19 +00:00
Basic type inference
This commit is contained in:
parent
ad298c7bea
commit
ca9c79185b
@ -1059,8 +1059,7 @@ export class Source extends Node {
|
|||||||
for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) {
|
for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) {
|
||||||
const statement: Statement = this.statements[i];
|
const statement: Statement = this.statements[i];
|
||||||
statement.serialize(sb);
|
statement.serialize(sb);
|
||||||
const last: string = sb[sb.length - 1];
|
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
|
||||||
if (last.charCodeAt(last.length - 1) == CharCode.CLOSEBRACE)
|
|
||||||
sb.push("\n");
|
sb.push("\n");
|
||||||
else
|
else
|
||||||
sb.push(";\n");
|
sb.push(";\n");
|
||||||
|
@ -324,48 +324,48 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
compileGlobal(global: Global): bool {
|
compileGlobal(global: Global): bool {
|
||||||
if (global.isCompiled)
|
if (global.isCompiled)
|
||||||
return true;
|
return true;
|
||||||
if (global.isBuiltIn)
|
if (global.isBuiltIn && compileBuiltinGetGlobal(this, global))
|
||||||
if (compileBuiltinGetGlobal(this, global))
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
const declaration: VariableLikeDeclarationStatement | null = global.declaration;
|
const declaration: VariableLikeDeclarationStatement | null = global.declaration;
|
||||||
let type: Type | null = global.type;
|
if (!global.type) {
|
||||||
if (!type) {
|
if (declaration && declaration.type) {
|
||||||
if (!declaration)
|
global.type = this.program.resolveType(declaration.type); // reports
|
||||||
throw new Error("unexpected missing declaration");
|
if (!global.type)
|
||||||
if (!declaration.type) { // TODO: infer type
|
return false;
|
||||||
this.error(DiagnosticCode.Type_expected, declaration.name.range);
|
} 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;
|
return false;
|
||||||
}
|
} else
|
||||||
type = this.program.resolveType(declaration.type); // reports
|
throw new Error("unable to infer type");
|
||||||
if (!type)
|
|
||||||
return false;
|
|
||||||
global.type = type;
|
|
||||||
}
|
}
|
||||||
if (this.module.noEmit)
|
if (this.module.noEmit)
|
||||||
return true;
|
return true;
|
||||||
const nativeType: NativeType = typeToNativeType(<Type>type);
|
const nativeType: NativeType = typeToNativeType(global.type);
|
||||||
let initializer: ExpressionRef;
|
let initializer: ExpressionRef;
|
||||||
let initializeInStart: bool = false;
|
let initializeInStart: bool = false;
|
||||||
if (global.hasConstantValue) {
|
if (global.hasConstantValue) {
|
||||||
assert(type != null);
|
if (global.type.isLongInteger)
|
||||||
if (type.isLongInteger)
|
|
||||||
initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0);
|
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);
|
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);
|
initializer = this.module.createF64(global.constantFloatValue);
|
||||||
else if (type.isSmallInteger) {
|
else if (global.type.isSmallInteger) {
|
||||||
if (type.isSignedInteger) {
|
if (global.type.isSignedInteger) {
|
||||||
const shift: i32 = type.smallIntegerShift;
|
const shift: i32 = global.type.smallIntegerShift;
|
||||||
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0);
|
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0);
|
||||||
} else
|
} 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
|
} else
|
||||||
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0);
|
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0);
|
||||||
} else if (declaration) {
|
} else if (declaration) {
|
||||||
if (declaration.initializer) {
|
if (declaration.initializer) {
|
||||||
initializer = this.compileExpression(declaration.initializer, type);
|
initializer = this.compileExpression(declaration.initializer, global.type);
|
||||||
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
|
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
|
||||||
if (!global.isMutable) {
|
if (!global.isMutable) {
|
||||||
initializer = this.precomputeExpressionRef(initializer);
|
initializer = this.precomputeExpressionRef(initializer);
|
||||||
@ -377,13 +377,13 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
initializeInStart = true;
|
initializeInStart = true;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
initializer = typeToNativeZero(this.module, type);
|
initializer = typeToNativeZero(this.module, global.type);
|
||||||
} else
|
} else
|
||||||
throw new Error("unexpected missing declaration or constant value");
|
throw new Error("unexpected missing declaration or constant value");
|
||||||
|
|
||||||
const internalName: string = global.internalName;
|
const internalName: string = global.internalName;
|
||||||
if (initializeInStart) {
|
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));
|
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
|
||||||
} else {
|
} else {
|
||||||
this.module.addGlobal(internalName, nativeType, global.isMutable, initializer);
|
this.module.addGlobal(internalName, nativeType, global.isMutable, initializer);
|
||||||
@ -717,7 +717,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
// types
|
// types
|
||||||
|
|
||||||
// TODO: try to get rid of this
|
// 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 previousType: Type = this.currentType;
|
||||||
const previousNoEmit: bool = this.module.noEmit;
|
const previousNoEmit: bool = this.module.noEmit;
|
||||||
this.module.noEmit = true;
|
this.module.noEmit = true;
|
||||||
@ -971,19 +971,26 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
const initializers: ExpressionRef[] = new Array();
|
const initializers: ExpressionRef[] = new Array();
|
||||||
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
|
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
|
||||||
const declaration: VariableDeclaration = declarations[i];
|
const declaration: VariableDeclaration = declarations[i];
|
||||||
|
const name: string = declaration.name.name;
|
||||||
|
let type: Type | null = null;
|
||||||
if (declaration.type) {
|
if (declaration.type) {
|
||||||
const name: string = declaration.name.name;
|
type = this.program.resolveType(<TypeNode>declaration.type, this.currentFunction.contextualTypeArguments, true); // reports
|
||||||
const type: Type | null = this.program.resolveType(<TypeNode>declaration.type, this.currentFunction.contextualTypeArguments, true); // reports
|
if (!type)
|
||||||
if (type) {
|
continue;
|
||||||
if (this.currentFunction.locals.has(name))
|
} else if (declaration.initializer) {
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, name); // recoverable
|
type = this.determineExpressionType(declaration.initializer); // reports
|
||||||
else
|
if (!type)
|
||||||
this.currentFunction.addLocal(<Type>type, name);
|
continue;
|
||||||
if (declaration.initializer)
|
|
||||||
initializers.push(this.compileAssignment(declaration.name, <Expression>declaration.initializer, Type.void));
|
|
||||||
}
|
|
||||||
} else {
|
} 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();
|
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)
|
if (conversionKind == ConversionKind.NONE)
|
||||||
return expr;
|
return expr;
|
||||||
|
|
||||||
if (!fromType) {
|
|
||||||
_BinaryenExpressionPrint(expr);
|
|
||||||
throw new Error("WHAT");
|
|
||||||
}
|
|
||||||
|
|
||||||
// void to any
|
// void to any
|
||||||
if (fromType.kind == TypeKind.VOID) {
|
if (fromType.kind == TypeKind.VOID) {
|
||||||
this.error(DiagnosticCode.Operation_not_supported, reportNode.range);
|
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);
|
return this.module.createI64(intValue.lo, intValue.hi);
|
||||||
if (contextualType.isSmallInteger)
|
if (contextualType.isSmallInteger)
|
||||||
return this.module.createI32(intValue.toI32());
|
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;
|
this.currentType = contextualType.isSignedInteger ? Type.i32 : Type.u32;
|
||||||
return this.module.createI32(intValue.toI32());
|
return this.module.createI32(intValue.toI32());
|
||||||
}
|
}
|
||||||
@ -1942,7 +1948,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
: this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
|
: this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
|
||||||
}
|
}
|
||||||
} else {
|
} 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();
|
return this.module.createUnreachable();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -155,8 +155,6 @@ export class Type {
|
|||||||
static readonly f64: Type = new Type(TypeKind.F64, 64);
|
static readonly f64: Type = new Type(TypeKind.F64, 64);
|
||||||
/** No return type. */
|
/** No return type. */
|
||||||
static readonly void: Type = new Type(TypeKind.VOID, 0);
|
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. */
|
/** Converts an array of types to its combined string representation. Usually type arguments. */
|
||||||
|
11
tests/compiler/infer-type.optimized-inlined.wast
Normal file
11
tests/compiler/infer-type.optimized-inlined.wast
Normal 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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
12
tests/compiler/infer-type.optimized.wast
Normal file
12
tests/compiler/infer-type.optimized.wast
Normal 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)
|
||||||
|
)
|
||||||
|
)
|
18
tests/compiler/infer-type.ts
Normal file
18
tests/compiler/infer-type.ts
Normal 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();
|
113
tests/compiler/infer-type.wast
Normal file
113
tests/compiler/infer-type.wast
Normal 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]
|
||||||
|
|
||||||
|
;)
|
4
tests/parser/continue-on-error.ts
Normal file
4
tests/parser/continue-on-error.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
var; while for let; a from "./other";
|
||||||
|
do {
|
||||||
|
;
|
||||||
|
} while (false);
|
12
tests/parser/continue-on-error.ts.fixture.ts
Normal file
12
tests/parser/continue-on-error.ts.fixture.ts
Normal 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
|
Loading…
x
Reference in New Issue
Block a user