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

View File

@ -16,7 +16,7 @@ export enum DiagnosticCode {
Type_0_cannot_be_reinterpreted_as_type_1 = 203, Type_0_cannot_be_reinterpreted_as_type_1 = 203,
Basic_type_0_cannot_be_nullable = 204, Basic_type_0_cannot_be_nullable = 204,
Cannot_export_a_mutable_global = 205, 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_extend_managed_classes_and_vice_versa = 207,
Unmanaged_classes_cannot_implement_interfaces = 208, Unmanaged_classes_cannot_implement_interfaces = 208,
Invalid_regular_expression_flags = 209, 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 203: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
case 204: return "Basic type '{0}' cannot be nullable."; case 204: return "Basic type '{0}' cannot be nullable.";
case 205: return "Cannot export a mutable global."; 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 207: return "Unmanaged classes cannot extend managed classes and vice-versa.";
case 208: return "Unmanaged classes cannot implement interfaces."; case 208: return "Unmanaged classes cannot implement interfaces.";
case 209: return "Invalid regular expression flags."; case 209: return "Invalid regular expression flags.";

View File

@ -8,7 +8,7 @@
"Type '{0}' cannot be reinterpreted as type '{1}'.": 203, "Type '{0}' cannot be reinterpreted as type '{1}'.": 203,
"Basic type '{0}' cannot be nullable.": 204, "Basic type '{0}' cannot be nullable.": 204,
"Cannot export a mutable global.": 205, "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 extend managed classes and vice-versa.": 207,
"Unmanaged classes cannot implement interfaces.": 208, "Unmanaged classes cannot implement interfaces.": 208,
"Invalid regular expression flags.": 209, "Invalid regular expression flags.": 209,

View File

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