Tackle AS206

This commit is contained in:
dcode 2019-05-27 18:44:15 +02:00
parent c06d5d9f9a
commit bc0dd3a6fb
8 changed files with 416 additions and 56 deletions

View File

@ -779,7 +779,7 @@ export class Compiler extends DiagnosticEmitter {
if (!global.is(CommonFlags.RESOLVED)) {
// resolve now if annotated
// Resolve type if annotated
if (typeNode) {
let resolvedType = this.resolver.resolveType(typeNode, global.parent); // reports
if (!resolvedType) return false;
@ -792,8 +792,8 @@ export class Compiler extends DiagnosticEmitter {
}
global.setType(resolvedType);
// infer from initializer if not annotated
} else if (initializerNode) { // infer type using void/NONE for literal inference
// Otherwise infer type from initializer
} else if (initializerNode) {
let previousFlow = this.currentFlow;
if (global.hasDecorator(DecoratorFlags.LAZY)) {
this.currentFlow = global.file.startFunction.flow;
@ -812,7 +812,7 @@ export class Compiler extends DiagnosticEmitter {
}
global.setType(this.currentType);
// must either be annotated or have an initializer
// Error if there's neither a type nor an initializer
} else {
this.error(
DiagnosticCode.Type_expected,
@ -822,7 +822,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
// ambient builtins like 'HEAP_BASE' need to be resolved but are added explicitly
// Handle ambient builtins like 'HEAP_BASE' that need to be resolved but are added explicitly
if (global.is(CommonFlags.AMBIENT) && global.hasDecorator(DecoratorFlags.BUILTIN)) {
if (global.internalName == BuiltinSymbols.HEAP_BASE) this.runtimeFeatures |= RuntimeFeatures.HEAP;
else if (global.internalName == BuiltinSymbols.RTTI_BASE) this.runtimeFeatures |= RuntimeFeatures.RTTI;
@ -832,11 +832,12 @@ export class Compiler extends DiagnosticEmitter {
var type = global.type;
var nativeType = type.toNativeType();
var isDeclaredConstant = global.is(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
var isDeclaredInline = global.hasDecorator(DecoratorFlags.INLINE);
// handle imports
// Handle imports
if (global.is(CommonFlags.AMBIENT)) {
// constant global
// Constant global or mutable globals enabled
if (isDeclaredConstant || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
global.set(CommonFlags.MODULE_IMPORT);
mangleImportName(global, global.declaration);
@ -849,7 +850,7 @@ export class Compiler extends DiagnosticEmitter {
global.set(CommonFlags.COMPILED);
return true;
// importing mutable globals is not supported in the MVP
// Importing mutable globals is not supported in the MVP
} else {
this.error(
DiagnosticCode.Operation_not_supported,
@ -859,11 +860,11 @@ export class Compiler extends DiagnosticEmitter {
return false;
}
// the MVP does not yet support initializer expressions other than constant values (and constant
// The MVP does not yet support initializer expressions other than constant values (and constant
// get_globals), hence such initializations must be performed in the start function for now.
var initializeInStart = false;
// evaluate initializer if present
// Evaluate initializer if present
if (initializerNode) {
if (!initExpr) {
let previousFlow = this.currentFlow;
@ -880,21 +881,20 @@ export class Compiler extends DiagnosticEmitter {
if (getExpressionId(initExpr) != ExpressionId.Const) {
if (isDeclaredConstant) {
initExpr = module.precomputeExpression(initExpr);
if (getExpressionId(initExpr) != ExpressionId.Const) {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
initializerNode.range
);
initializeInStart = true;
}
if (getExpressionId(initExpr) != ExpressionId.Const) initializeInStart = true;
} else {
initializeInStart = true;
}
}
// explicitly inline if annotated
if (global.hasDecorator(DecoratorFlags.INLINE)) {
if (!initializeInStart) { // reported above
// Explicitly inline if annotated
if (isDeclaredInline) {
if (initializeInStart) {
this.warning(
DiagnosticCode.Mutable_value_cannot_be_inlined,
initializerNode.range
);
} else {
assert(getExpressionId(initExpr) == ExpressionId.Const);
let exprType = getExpressionType(initExpr);
switch (exprType) {
@ -930,7 +930,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
// initialize to zero if there's no initializer
// Initialize to zero if there's no initializer
} else {
initExpr = type.toNativeZero(module);
}
@ -938,7 +938,7 @@ export class Compiler extends DiagnosticEmitter {
var internalName = global.internalName;
if (initializeInStart) { // initialize to mutable zero and set the actual value in start
if (global.hasDecorator(DecoratorFlags.INLINE)) {
if (isDeclaredInline) {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
assert(findDecorator(DecoratorKind.INLINE, global.decoratorNodes)).range, "inline"
@ -949,7 +949,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentBody.push(
module.global_set(internalName, initExpr)
);
} else if (!global.hasDecorator(DecoratorFlags.INLINE)) { // compile normally
} else if (!isDeclaredInline) { // compile normally
module.addGlobal(internalName, nativeType, !isDeclaredConstant, initExpr);
}
return true;
@ -2406,12 +2406,15 @@ export class Compiler extends DiagnosticEmitter {
var flow = this.currentFlow;
var initializers = new Array<ExpressionRef>();
var resolver = this.resolver;
for (let i = 0; i < numDeclarations; ++i) {
let declaration = declarations[i];
let name = declaration.name.text;
let type: Type | null = null;
let initExpr: ExpressionRef = 0;
let initAutoreleaseSkipped = false;
// Resolve type if annotated
if (declaration.type) {
type = resolver.resolveType( // reports
declaration.type,
@ -2425,7 +2428,9 @@ export class Compiler extends DiagnosticEmitter {
);
initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr);
}
} else if (declaration.initializer) { // infer type using void/NONE for proper literal inference
// Otherwise infer type from initializer
} else if (declaration.initializer) {
initExpr = this.compileExpressionRetainType(declaration.initializer, Type.void,
ContextualFlags.SKIP_AUTORELEASE
); // reports
@ -2438,6 +2443,8 @@ export class Compiler extends DiagnosticEmitter {
continue;
}
type = this.currentType;
// Error if there's neither a type nor an initializer
} else {
this.error(
DiagnosticCode.Type_expected,
@ -2445,8 +2452,11 @@ export class Compiler extends DiagnosticEmitter {
);
continue;
}
let isInlined = false;
if (declaration.is(CommonFlags.CONST)) {
// Handle constants, and try to inline if value is static
let isConst = declaration.is(CommonFlags.CONST);
let isStatic = false;
if (isConst) {
if (initExpr) {
initExpr = module.precomputeExpression(initExpr);
if (getExpressionId(initExpr) == ExpressionId.Const) {
@ -2496,12 +2506,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.unreachable();
}
scopedLocals.set(name, local);
isInlined = true;
} else {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
declaration.range
);
isStatic = true;
}
} else {
this.error(
@ -2510,13 +2515,16 @@ export class Compiler extends DiagnosticEmitter {
);
}
}
if (!isInlined) {
// Otherwise compile as mutable
if (!isStatic) {
let local: Local;
if (
declaration.isAny(CommonFlags.LET | CommonFlags.CONST) ||
flow.is(FlowFlags.INLINE_CONTEXT)
) { // here: not top-level
local = flow.addScopedLocal(name, type, declaration.name); // reports if duplicate
if (isConst) flow.setLocalFlag(local.index, LocalFlags.CONSTANT);
} else {
if (flow.lookupLocal(name)) {
this.error(
@ -2526,6 +2534,7 @@ export class Compiler extends DiagnosticEmitter {
continue;
}
local = flow.parentFunction.addLocal(type, name, declaration);
if (isConst) flow.setLocalFlag(local.index, LocalFlags.CONSTANT);
}
let isManaged = type.isManaged;
if (initExpr) {
@ -5234,7 +5243,7 @@ export class Compiler extends DiagnosticEmitter {
switch (target.kind) {
case ElementKind.LOCAL: {
if (target.is(CommonFlags.CONST)) {
if (flow.isLocalFlag((<Local>target).index, LocalFlags.CONSTANT, true)) {
this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
valueExpression.range, target.internalName
@ -5246,7 +5255,7 @@ export class Compiler extends DiagnosticEmitter {
}
case ElementKind.GLOBAL: {
if (!this.compileGlobal(<Global>target)) return module.unreachable();
if (target.is(CommonFlags.CONST)) {
if (target.isAny(CommonFlags.CONST | CommonFlags.READONLY)) {
this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
valueExpression.range,
@ -7534,7 +7543,7 @@ export class Compiler extends DiagnosticEmitter {
// compile value expressions and find out whether all are constant
var length = expressions.length;
var values = new Array<ExpressionRef>(length);
var allValuesAreConstant = true;
var isStatic = true;
var nativeElementType = elementType.toNativeType();
for (let i = 0; i < length; ++i) {
let expression = expressions[i];
@ -7548,19 +7557,13 @@ export class Compiler extends DiagnosticEmitter {
if (getExpressionId(expr) == ExpressionId.Const) {
assert(getExpressionType(expr) == nativeElementType);
} else {
if (isConst) {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
reportNode.range
);
}
allValuesAreConstant = false;
isStatic = false;
}
values[i] = expr;
}
// if the array is static, make a static arraybuffer segment
if (allValuesAreConstant) {
if (isStatic) {
flow.freeTempLocal(tempThis);
flow.freeTempLocal(tempDataStart);

View File

@ -16,7 +16,7 @@ export enum DiagnosticCode {
Type_0_cannot_be_reinterpreted_as_type_1 = 203,
Basic_type_0_cannot_be_nullable = 204,
Cannot_export_a_mutable_global = 205,
Compiling_constant_with_non_constant_initializer_as_mutable = 206,
Mutable_value_cannot_be_inlined = 206,
Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa = 207,
Unmanaged_classes_cannot_implement_interfaces = 208,
Invalid_regular_expression_flags = 209,
@ -154,7 +154,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 203: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
case 204: return "Basic type '{0}' cannot be nullable.";
case 205: return "Cannot export a mutable global.";
case 206: return "Compiling constant with non-constant initializer as mutable.";
case 206: return "Mutable value cannot be inlined.";
case 207: return "Unmanaged classes cannot extend managed classes and vice-versa.";
case 208: return "Unmanaged classes cannot implement interfaces.";
case 209: return "Invalid regular expression flags.";

View File

@ -8,7 +8,7 @@
"Type '{0}' cannot be reinterpreted as type '{1}'.": 203,
"Basic type '{0}' cannot be nullable.": 204,
"Cannot export a mutable global.": 205,
"Compiling constant with non-constant initializer as mutable.": 206,
"Mutable value cannot be inlined.": 206,
"Unmanaged classes cannot extend managed classes and vice-versa.": 207,
"Unmanaged classes cannot implement interfaces.": 208,
"Invalid regular expression flags.": 209,

View File

@ -143,26 +143,29 @@ export enum LocalFlags {
/** No specific conditions. */
NONE = 0,
/** Local is constant. */
CONSTANT = 1 << 0,
/** Local is properly wrapped. Relevant for small integers. */
WRAPPED = 1 << 0,
WRAPPED = 1 << 1,
/** Local is non-null. */
NONNULL = 1 << 1,
NONNULL = 1 << 2,
/** Local is read from. */
READFROM = 1 << 2,
READFROM = 1 << 3,
/** Local is written to. */
WRITTENTO = 1 << 3,
WRITTENTO = 1 << 4,
/** Local is retained. */
RETAINED = 1 << 4,
RETAINED = 1 << 5,
/** Local is conditionally read from. */
CONDITIONALLY_READFROM = 1 << 5,
CONDITIONALLY_READFROM = 1 << 6,
/** Local is conditionally written to. */
CONDITIONALLY_WRITTENTO = 1 << 6,
CONDITIONALLY_WRITTENTO = 1 << 7,
/** Local must be conditionally retained. */
CONDITIONALLY_RETAINED = 1 << 7,
CONDITIONALLY_RETAINED = 1 << 8,
/** Any categorical flag. */
ANY_CATEGORICAL = WRAPPED
ANY_CATEGORICAL = CONSTANT
| WRAPPED
| NONNULL
| READFROM
| WRITTENTO

View File

@ -0,0 +1,5 @@
{
"asc_flags": [
"--runtime none"
]
}

View File

@ -0,0 +1,319 @@
(module
(type $FUNCSIG$iii (func (param i32 i32) (result i32)))
(type $FUNCSIG$v (func))
(type $FUNCSIG$vii (func (param i32 i32)))
(type $FUNCSIG$ii (func (param i32) (result i32)))
(memory $0 1)
(data (i32.const 8) "\04\00\00\00\01\00\00\00\00\00\00\00\04\00\00\00\02")
(data (i32.const 32) "\04\00\00\00\01\00\00\00\00\00\00\00\04\00\00\00\01")
(data (i32.const 56) "\04\00\00\00\01\00\00\00\00\00\00\00\04\00\00\00\02")
(global $~lib/rt/stub/startOffset (mut i32) (i32.const 0))
(global $~lib/rt/stub/offset (mut i32) (i32.const 0))
(export "memory" (memory $0))
(start $start)
(func $~lib/rt/stub/__alloc (; 0 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
local.get $0
i32.const 1073741808
i32.gt_u
if
unreachable
end
global.get $~lib/rt/stub/offset
i32.const 16
i32.add
local.tee $3
local.get $0
i32.const 1
local.get $0
i32.const 1
i32.gt_u
select
i32.add
i32.const 15
i32.add
i32.const -16
i32.and
local.tee $2
memory.size
local.tee $4
i32.const 16
i32.shl
i32.gt_u
if
local.get $4
local.get $2
local.get $3
i32.sub
i32.const 65535
i32.add
i32.const -65536
i32.and
i32.const 16
i32.shr_u
local.tee $5
local.get $4
local.get $5
i32.gt_s
select
memory.grow
i32.const 0
i32.lt_s
if
local.get $5
memory.grow
i32.const 0
i32.lt_s
if
unreachable
end
end
end
local.get $2
global.set $~lib/rt/stub/offset
local.get $3
i32.const 16
i32.sub
local.tee $2
local.get $1
i32.store offset=8
local.get $2
local.get $0
i32.store offset=12
local.get $3
)
(func $~lib/memory/memory.copy (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
block $~lib/util/memory/memmove|inlined.0
i32.const 4
local.set $2
local.get $0
local.get $1
i32.eq
br_if $~lib/util/memory/memmove|inlined.0
local.get $0
local.get $1
i32.lt_u
if
local.get $1
i32.const 7
i32.and
local.get $0
i32.const 7
i32.and
i32.eq
if
loop $continue|0
local.get $0
i32.const 7
i32.and
if
local.get $2
i32.eqz
br_if $~lib/util/memory/memmove|inlined.0
local.get $2
i32.const 1
i32.sub
local.set $2
local.get $0
local.tee $3
i32.const 1
i32.add
local.set $0
local.get $1
local.tee $4
i32.const 1
i32.add
local.set $1
local.get $3
local.get $4
i32.load8_u
i32.store8
br $continue|0
end
end
loop $continue|1
local.get $2
i32.const 8
i32.ge_u
if
local.get $0
local.get $1
i64.load
i64.store
local.get $2
i32.const 8
i32.sub
local.set $2
local.get $0
i32.const 8
i32.add
local.set $0
local.get $1
i32.const 8
i32.add
local.set $1
br $continue|1
end
end
end
loop $continue|2
local.get $2
if
local.get $0
local.tee $3
i32.const 1
i32.add
local.set $0
local.get $1
local.tee $4
i32.const 1
i32.add
local.set $1
local.get $3
local.get $4
i32.load8_u
i32.store8
local.get $2
i32.const 1
i32.sub
local.set $2
br $continue|2
end
end
else
local.get $1
i32.const 7
i32.and
local.get $0
i32.const 7
i32.and
i32.eq
if
loop $continue|3
local.get $0
local.get $2
i32.add
i32.const 7
i32.and
if
local.get $2
i32.eqz
br_if $~lib/util/memory/memmove|inlined.0
local.get $2
i32.const 1
i32.sub
local.tee $2
local.get $0
i32.add
local.get $1
local.get $2
i32.add
i32.load8_u
i32.store8
br $continue|3
end
end
loop $continue|4
local.get $2
i32.const 8
i32.ge_u
if
local.get $2
i32.const 8
i32.sub
local.tee $2
local.get $0
i32.add
local.get $1
local.get $2
i32.add
i64.load
i64.store
br $continue|4
end
end
end
loop $continue|5
local.get $2
if
local.get $2
i32.const 1
i32.sub
local.tee $2
local.get $0
i32.add
local.get $1
local.get $2
i32.add
i32.load8_u
i32.store8
br $continue|5
end
end
end
end
)
(func $~lib/rt/__allocArray (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32)
(local $2 i32)
i32.const 16
i32.const 3
call $~lib/rt/stub/__alloc
local.tee $1
i32.const 4
i32.const 0
call $~lib/rt/stub/__alloc
local.tee $2
i32.store
local.get $1
local.get $2
i32.store offset=4
local.get $1
i32.const 4
i32.store offset=8
local.get $1
i32.const 1
i32.store offset=12
local.get $0
if
local.get $2
local.get $0
call $~lib/memory/memory.copy
end
local.get $1
)
(func $start:constant-assign (; 3 ;) (type $FUNCSIG$v)
i32.const 80
global.set $~lib/rt/stub/startOffset
global.get $~lib/rt/stub/startOffset
global.set $~lib/rt/stub/offset
i32.const 0
call $~lib/rt/__allocArray
i32.load offset=4
i32.const 1
i32.store
i32.const 24
call $~lib/rt/__allocArray
drop
i32.const 48
call $~lib/rt/__allocArray
drop
i32.const 72
call $~lib/rt/__allocArray
drop
)
(func $start (; 4 ;) (type $FUNCSIG$v)
block
call $start:constant-assign
end
)
(func $null (; 5 ;) (type $FUNCSIG$v)
nop
)
)

View File

@ -0,0 +1,30 @@
// Expect error: TS2540
// ^ TODO: Properly handle multiple
function localConst(a: i32): void {
const b = a + 1;
b = 3;
}
localConst(1);
function localConstInline(): void {
const a = 1;
a = 2;
}
localConstInline();
function localConstArray(a: i32): void {
const b: i32[] = [ a ];
b = [ 2 ];
}
localConstArray(1);
function localConstArrayInline(): void {
const a: i32[] = [ 1 ];
a = [ 2 ];
}
localConstArrayInline();
// globalConst
const a = 1;
a = 2;