mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +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) {
|
||||
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");
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
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