From 6a2c4dc625cd1be25fab3f6ea1ed89eaf17ef96f Mon Sep 17 00:00:00 2001 From: dcode Date: Sat, 21 Dec 2019 13:59:37 +0100 Subject: [PATCH 1/4] Infer array type from first element in auto context --- src/compiler.ts | 40 +- tests/compiler/infer-array.json | 5 + tests/compiler/infer-array.optimized.wat | 382 ++++++ tests/compiler/infer-array.ts | 16 + tests/compiler/infer-array.untouched.wat | 1595 ++++++++++++++++++++++ 5 files changed, 2029 insertions(+), 9 deletions(-) create mode 100644 tests/compiler/infer-array.json create mode 100644 tests/compiler/infer-array.optimized.wat create mode 100644 tests/compiler/infer-array.ts create mode 100644 tests/compiler/infer-array.untouched.wat diff --git a/src/compiler.ts b/src/compiler.ts index 367fcfa95e..349bd4d81e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7455,17 +7455,39 @@ export class Compiler extends DiagnosticEmitter { switch (expression.literalKind) { case LiteralKind.ARRAY: { assert(!implicitlyNegate); - let classType = contextualType.classReference; - if (classType) { - if (classType.prototype == this.program.arrayPrototype) { - return this.compileArrayLiteral( - assert(classType.typeArguments)[0], - (expression).elementExpressions, - constraints, - expression - ); + let elementExpressions = (expression).elementExpressions; + + // Infer from first element in auto contexts + if (contextualType == Type.auto) { + if (elementExpressions.length) { + let first = elementExpressions[0]; + if (first) { + let firstType = this.resolver.resolveExpression(first, this.currentFlow); + if (!firstType) return module.unreachable(); + return this.compileArrayLiteral( + firstType, + elementExpressions, + constraints, + expression + ); + } + } + + // Use contextual type if an array + } else if (contextualType.is(TypeFlags.REFERENCE)) { + let classType = contextualType.classReference; + if (classType) { + if (classType.prototype == this.program.arrayPrototype) { + return this.compileArrayLiteral( + assert(classType.typeArguments)[0], + elementExpressions, + constraints, + expression + ); + } } } + this.error( DiagnosticCode.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly, expression.range, "T" diff --git a/tests/compiler/infer-array.json b/tests/compiler/infer-array.json new file mode 100644 index 0000000000..b1da366ff4 --- /dev/null +++ b/tests/compiler/infer-array.json @@ -0,0 +1,5 @@ +{ + "asc_flags": [ + "--runtime none" + ] +} \ No newline at end of file diff --git a/tests/compiler/infer-array.optimized.wat b/tests/compiler/infer-array.optimized.wat new file mode 100644 index 0000000000..8b127e5806 --- /dev/null +++ b/tests/compiler/infer-array.optimized.wat @@ -0,0 +1,382 @@ +(module + (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data (i32.const 8) "\0c\00\00\00\01\00\00\00\00\00\00\00\0c\00\00\00\01\00\00\00\02\00\00\00\03") + (data (i32.const 40) "$\00\00\00\01\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") + (data (i32.const 96) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s") + (data (i32.const 144) "\18\00\00\00\01\00\00\00\00\00\00\00\18") + (data (i32.const 166) "\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") + (data (i32.const 184) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s") + (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/maybeGrowMemory (; 1 ;) (param $0 i32) + (local $1 i32) + (local $2 i32) + local.get $0 + memory.size + local.tee $2 + i32.const 16 + i32.shl + local.tee $1 + i32.gt_u + if + local.get $2 + local.get $0 + local.get $1 + i32.sub + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $1 + local.get $2 + local.get $1 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $1 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/rt/stub/offset + ) + (func $~lib/rt/stub/__alloc (; 2 ;) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 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 15 + i32.add + i32.const -16 + i32.and + local.tee $2 + i32.const 16 + local.get $2 + i32.const 16 + i32.gt_u + select + local.tee $4 + i32.add + call $~lib/rt/stub/maybeGrowMemory + local.get $3 + i32.const 16 + i32.sub + local.tee $2 + local.get $4 + i32.store + local.get $2 + i32.const -1 + i32.store offset=4 + local.get $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 (; 3 ;) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + block $~lib/util/memory/memmove|inlined.0 + local.get $2 + local.set $3 + 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 $3 + i32.eqz + br_if $~lib/util/memory/memmove|inlined.0 + local.get $3 + i32.const 1 + i32.sub + local.set $3 + local.get $0 + local.tee $2 + i32.const 1 + i32.add + local.set $0 + local.get $1 + local.tee $4 + i32.const 1 + i32.add + local.set $1 + local.get $2 + local.get $4 + i32.load8_u + i32.store8 + br $continue|0 + end + end + loop $continue|1 + local.get $3 + i32.const 8 + i32.lt_u + i32.eqz + if + local.get $0 + local.get $1 + i64.load + i64.store + local.get $3 + i32.const 8 + i32.sub + local.set $3 + 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 $3 + if + local.get $0 + local.tee $2 + i32.const 1 + i32.add + local.set $0 + local.get $1 + local.tee $4 + i32.const 1 + i32.add + local.set $1 + local.get $2 + local.get $4 + i32.load8_u + i32.store8 + local.get $3 + i32.const 1 + i32.sub + local.set $3 + 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 $3 + i32.add + i32.const 7 + i32.and + if + local.get $3 + i32.eqz + br_if $~lib/util/memory/memmove|inlined.0 + local.get $0 + local.get $3 + i32.const 1 + i32.sub + local.tee $3 + i32.add + local.get $1 + local.get $3 + i32.add + i32.load8_u + i32.store8 + br $continue|3 + end + end + loop $continue|4 + local.get $3 + i32.const 8 + i32.lt_u + i32.eqz + if + local.get $0 + local.get $3 + i32.const 8 + i32.sub + local.tee $3 + i32.add + local.get $1 + local.get $3 + i32.add + i64.load + i64.store + br $continue|4 + end + end + end + loop $continue|5 + local.get $3 + if + local.get $0 + local.get $3 + i32.const 1 + i32.sub + local.tee $3 + i32.add + local.get $1 + local.get $3 + i32.add + i32.load8_u + i32.store8 + br $continue|5 + end + end + end + end + ) + (func $~lib/rt/__allocArray (; 4 ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) + i32.const 16 + local.get $2 + call $~lib/rt/stub/__alloc + local.tee $2 + local.get $0 + local.get $1 + i32.shl + local.tee $1 + i32.const 0 + call $~lib/rt/stub/__alloc + local.tee $4 + i32.store + local.get $2 + local.get $4 + i32.store offset=4 + local.get $2 + local.get $1 + i32.store offset=8 + local.get $2 + local.get $0 + i32.store offset=12 + local.get $3 + if + local.get $4 + local.get $3 + local.get $1 + call $~lib/memory/memory.copy + end + local.get $2 + ) + (func $~lib/array/Array#__get (; 5 ;) (param $0 i32) (result i32) + i32.const 1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + i32.const 4 + i32.add + i32.load + ) + (func $start:infer-array (; 6 ;) + (local $0 i32) + (local $1 i32) + i32.const 240 + global.set $~lib/rt/stub/startOffset + i32.const 240 + global.set $~lib/rt/stub/offset + i32.const 3 + i32.const 2 + i32.const 3 + i32.const 24 + call $~lib/rt/__allocArray + drop + i32.const 3 + i32.const 3 + i32.const 4 + i32.const 160 + call $~lib/rt/__allocArray + drop + i32.const 2 + i32.const 2 + i32.const 5 + i32.const 0 + call $~lib/rt/__allocArray + local.tee $0 + i32.load offset=4 + local.tee $1 + i32.const 0 + i32.store + local.get $1 + i32.const -1 + i32.store offset=4 + local.get $0 + call $~lib/array/Array#__get + i32.const -1 + i32.ne + if + i32.const 0 + i32.const 200 + i32.const 15 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + ) + (func $start (; 7 ;) + call $start:infer-array + ) +) diff --git a/tests/compiler/infer-array.ts b/tests/compiler/infer-array.ts new file mode 100644 index 0000000000..497ec2da97 --- /dev/null +++ b/tests/compiler/infer-array.ts @@ -0,0 +1,16 @@ +{ + let arr = [1, 2, 3]; + assert(isInteger(arr[0])); + assert(isSigned(arr[0])); +} +{ + let arr = [1.0, 2, 3]; + assert(isFloat(arr[0])); +} +{ + let a: u32 = 0; + let arr = [ a, -1 ]; + assert(isInteger(arr[0])); + assert(!isSigned(arr[0])); + assert(arr[1] == 4294967295); +} diff --git a/tests/compiler/infer-array.untouched.wat b/tests/compiler/infer-array.untouched.wat new file mode 100644 index 0000000000..f27a1c4278 --- /dev/null +++ b/tests/compiler/infer-array.untouched.wat @@ -0,0 +1,1595 @@ +(module + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data (i32.const 8) "\0c\00\00\00\01\00\00\00\00\00\00\00\0c\00\00\00\01\00\00\00\02\00\00\00\03\00\00\00") + (data (i32.const 40) "$\00\00\00\01\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00") + (data (i32.const 96) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00") + (data (i32.const 144) "\18\00\00\00\01\00\00\00\00\00\00\00\18\00\00\00\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") + (data (i32.const 184) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s\00") + (table $0 1 funcref) + (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) + (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) + (global $~lib/ASC_SHRINK_LEVEL i32 (i32.const 0)) + (global $~lib/heap/__heap_base i32 (i32.const 228)) + (export "memory" (memory $0)) + (start $start) + (func $~lib/rt/stub/maybeGrowMemory (; 1 ;) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + memory.size + local.set $1 + local.get $1 + i32.const 16 + i32.shl + local.set $2 + local.get $0 + local.get $2 + i32.gt_u + if + local.get $0 + local.get $2 + i32.sub + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $3 + local.get $1 + local.tee $4 + local.get $3 + local.tee $5 + local.get $4 + local.get $5 + i32.gt_s + select + local.set $4 + local.get $4 + memory.grow + i32.const 0 + i32.lt_s + if + local.get $3 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/rt/stub/offset + ) + (func $~lib/rt/stub/__alloc (; 2 ;) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 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.set $2 + local.get $0 + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.tee $3 + i32.const 16 + local.tee $4 + local.get $3 + local.get $4 + i32.gt_u + select + local.set $5 + local.get $2 + local.get $5 + i32.add + call $~lib/rt/stub/maybeGrowMemory + local.get $2 + i32.const 16 + i32.sub + local.set $6 + local.get $6 + local.get $5 + i32.store + local.get $6 + i32.const -1 + i32.store offset=4 + local.get $6 + local.get $1 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $2 + ) + (func $~lib/rt/stub/__retain (; 3 ;) (param $0 i32) (result i32) + local.get $0 + ) + (func $~lib/util/memory/memcpy (; 4 ;) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + block $break|0 + loop $continue|0 + local.get $2 + if (result i32) + local.get $1 + i32.const 3 + i32.and + else + i32.const 0 + end + i32.eqz + br_if $break|0 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br $continue|0 + end + unreachable + end + local.get $0 + i32.const 3 + i32.and + i32.const 0 + i32.eq + if + block $break|1 + loop $continue|1 + local.get $2 + i32.const 16 + i32.ge_u + i32.eqz + br_if $break|1 + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.get $1 + i32.const 4 + i32.add + i32.load + i32.store + local.get $0 + i32.const 8 + i32.add + local.get $1 + i32.const 8 + i32.add + i32.load + i32.store + local.get $0 + i32.const 12 + i32.add + local.get $1 + i32.const 12 + i32.add + i32.load + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $continue|1 + end + unreachable + end + local.get $2 + i32.const 8 + i32.and + if + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.get $1 + i32.const 4 + i32.add + i32.load + i32.store + local.get $0 + i32.const 8 + i32.add + local.set $0 + local.get $1 + i32.const 8 + i32.add + local.set $1 + end + local.get $2 + i32.const 4 + i32.and + if + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + local.get $1 + i32.const 4 + i32.add + local.set $1 + end + local.get $2 + i32.const 2 + i32.and + if + local.get $0 + local.get $1 + i32.load16_u + i32.store16 + local.get $0 + i32.const 2 + i32.add + local.set $0 + local.get $1 + i32.const 2 + i32.add + local.set $1 + end + local.get $2 + i32.const 1 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + return + end + local.get $2 + i32.const 32 + i32.ge_u + if + block $break|2 + block $case2|2 + block $case1|2 + block $case0|2 + local.get $0 + i32.const 3 + i32.and + local.set $5 + local.get $5 + i32.const 1 + i32.eq + br_if $case0|2 + local.get $5 + i32.const 2 + i32.eq + br_if $case1|2 + local.get $5 + i32.const 3 + i32.eq + br_if $case2|2 + br $break|2 + end + local.get $1 + i32.load + local.set $3 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 3 + i32.sub + local.set $2 + block $break|3 + loop $continue|3 + local.get $2 + i32.const 17 + i32.ge_u + i32.eqz + br_if $break|3 + local.get $1 + i32.const 1 + i32.add + i32.load + local.set $4 + local.get $0 + local.get $3 + i32.const 24 + i32.shr_u + local.get $4 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 5 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.const 24 + i32.shr_u + local.get $3 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 9 + i32.add + i32.load + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.get $3 + i32.const 24 + i32.shr_u + local.get $4 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 13 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.const 24 + i32.shr_u + local.get $3 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $continue|3 + end + unreachable + end + br $break|2 + end + local.get $1 + i32.load + local.set $3 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 2 + i32.sub + local.set $2 + block $break|4 + loop $continue|4 + local.get $2 + i32.const 18 + i32.ge_u + i32.eqz + br_if $break|4 + local.get $1 + i32.const 2 + i32.add + i32.load + local.set $4 + local.get $0 + local.get $3 + i32.const 16 + i32.shr_u + local.get $4 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 6 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.const 16 + i32.shr_u + local.get $3 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 10 + i32.add + i32.load + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.get $3 + i32.const 16 + i32.shr_u + local.get $4 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 14 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.const 16 + i32.shr_u + local.get $3 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $continue|4 + end + unreachable + end + br $break|2 + end + local.get $1 + i32.load + local.set $3 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + block $break|5 + loop $continue|5 + local.get $2 + i32.const 19 + i32.ge_u + i32.eqz + br_if $break|5 + local.get $1 + i32.const 3 + i32.add + i32.load + local.set $4 + local.get $0 + local.get $3 + i32.const 8 + i32.shr_u + local.get $4 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 7 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.const 8 + i32.shr_u + local.get $3 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 11 + i32.add + i32.load + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.get $3 + i32.const 8 + i32.shr_u + local.get $4 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 15 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.const 8 + i32.shr_u + local.get $3 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $continue|5 + end + unreachable + end + br $break|2 + end + end + local.get $2 + i32.const 16 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 8 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 4 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 2 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 1 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + ) + (func $~lib/memory/memory.copy (; 5 ;) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + block $~lib/util/memory/memmove|inlined.0 + local.get $0 + local.set $5 + local.get $1 + local.set $4 + local.get $2 + local.set $3 + local.get $5 + local.get $4 + i32.eq + if + br $~lib/util/memory/memmove|inlined.0 + end + local.get $4 + local.get $3 + i32.add + local.get $5 + i32.le_u + if (result i32) + i32.const 1 + else + local.get $5 + local.get $3 + i32.add + local.get $4 + i32.le_u + end + if + local.get $5 + local.get $4 + local.get $3 + call $~lib/util/memory/memcpy + br $~lib/util/memory/memmove|inlined.0 + end + local.get $5 + local.get $4 + i32.lt_u + if + local.get $4 + i32.const 7 + i32.and + local.get $5 + i32.const 7 + i32.and + i32.eq + if + block $break|0 + loop $continue|0 + local.get $5 + i32.const 7 + i32.and + i32.eqz + br_if $break|0 + local.get $3 + i32.eqz + if + br $~lib/util/memory/memmove|inlined.0 + end + local.get $3 + i32.const 1 + i32.sub + local.set $3 + local.get $5 + local.tee $6 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $4 + local.tee $6 + i32.const 1 + i32.add + local.set $4 + local.get $6 + i32.load8_u + i32.store8 + br $continue|0 + end + unreachable + end + block $break|1 + loop $continue|1 + local.get $3 + i32.const 8 + i32.ge_u + i32.eqz + br_if $break|1 + local.get $5 + local.get $4 + i64.load + i64.store + local.get $3 + i32.const 8 + i32.sub + local.set $3 + local.get $5 + i32.const 8 + i32.add + local.set $5 + local.get $4 + i32.const 8 + i32.add + local.set $4 + br $continue|1 + end + unreachable + end + end + block $break|2 + loop $continue|2 + local.get $3 + i32.eqz + br_if $break|2 + local.get $5 + local.tee $6 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $4 + local.tee $6 + i32.const 1 + i32.add + local.set $4 + local.get $6 + i32.load8_u + i32.store8 + local.get $3 + i32.const 1 + i32.sub + local.set $3 + br $continue|2 + end + unreachable + end + else + local.get $4 + i32.const 7 + i32.and + local.get $5 + i32.const 7 + i32.and + i32.eq + if + block $break|3 + loop $continue|3 + local.get $5 + local.get $3 + i32.add + i32.const 7 + i32.and + i32.eqz + br_if $break|3 + local.get $3 + i32.eqz + if + br $~lib/util/memory/memmove|inlined.0 + end + local.get $5 + local.get $3 + i32.const 1 + i32.sub + local.tee $3 + i32.add + local.get $4 + local.get $3 + i32.add + i32.load8_u + i32.store8 + br $continue|3 + end + unreachable + end + block $break|4 + loop $continue|4 + local.get $3 + i32.const 8 + i32.ge_u + i32.eqz + br_if $break|4 + local.get $3 + i32.const 8 + i32.sub + local.set $3 + local.get $5 + local.get $3 + i32.add + local.get $4 + local.get $3 + i32.add + i64.load + i64.store + br $continue|4 + end + unreachable + end + end + block $break|5 + loop $continue|5 + local.get $3 + i32.eqz + br_if $break|5 + local.get $5 + local.get $3 + i32.const 1 + i32.sub + local.tee $3 + i32.add + local.get $4 + local.get $3 + i32.add + i32.load8_u + i32.store8 + br $continue|5 + end + unreachable + end + end + end + ) + (func $~lib/rt/__allocArray (; 6 ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + i32.const 16 + local.get $2 + call $~lib/rt/stub/__alloc + local.set $4 + local.get $0 + local.get $1 + i32.shl + local.set $5 + local.get $5 + i32.const 0 + call $~lib/rt/stub/__alloc + local.set $6 + local.get $4 + local.get $6 + call $~lib/rt/stub/__retain + i32.store + local.get $4 + local.get $6 + i32.store offset=4 + local.get $4 + local.get $5 + i32.store offset=8 + local.get $4 + local.get $0 + i32.store offset=12 + local.get $3 + if + local.get $6 + local.get $3 + local.get $5 + call $~lib/memory/memory.copy + end + local.get $4 + ) + (func $~lib/array/Array#__unchecked_get (; 7 ;) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 2 + i32.shl + i32.add + i32.load + ) + (func $~lib/array/Array#__get (; 8 ;) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/array/Array#__unchecked_get + local.set $2 + local.get $2 + ) + (func $~lib/rt/stub/__release (; 9 ;) (param $0 i32) + nop + ) + (func $~lib/array/Array#__unchecked_get (; 10 ;) (param $0 i32) (param $1 i32) (result f64) + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 3 + i32.shl + i32.add + f64.load + ) + (func $~lib/array/Array#__get (; 11 ;) (param $0 i32) (param $1 i32) (result f64) + (local $2 f64) + local.get $1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/array/Array#__unchecked_get + local.set $2 + local.get $2 + ) + (func $~lib/array/Array#__unchecked_get (; 12 ;) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 2 + i32.shl + i32.add + i32.load + ) + (func $~lib/array/Array#__get (; 13 ;) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/array/Array#__unchecked_get + local.set $2 + local.get $2 + ) + (func $start:infer-array (; 14 ;) + (local $0 i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/heap/__heap_base + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + global.set $~lib/rt/stub/startOffset + global.get $~lib/rt/stub/startOffset + global.set $~lib/rt/stub/offset + i32.const 3 + i32.const 2 + i32.const 3 + i32.const 24 + call $~lib/rt/__allocArray + call $~lib/rt/stub/__retain + local.tee $1 + call $~lib/rt/stub/__retain + local.set $0 + local.get $1 + call $~lib/rt/stub/__release + local.get $0 + call $~lib/rt/stub/__release + i32.const 3 + i32.const 3 + i32.const 4 + i32.const 160 + call $~lib/rt/__allocArray + call $~lib/rt/stub/__retain + local.tee $1 + call $~lib/rt/stub/__retain + local.set $0 + local.get $1 + call $~lib/rt/stub/__release + local.get $0 + call $~lib/rt/stub/__release + i32.const 0 + local.set $0 + i32.const 2 + i32.const 2 + i32.const 5 + i32.const 0 + call $~lib/rt/__allocArray + local.set $1 + local.get $1 + i32.load offset=4 + local.set $2 + local.get $2 + local.get $0 + i32.store + local.get $2 + i32.const -1 + i32.store offset=4 + local.get $1 + call $~lib/rt/stub/__retain + local.set $2 + local.get $2 + i32.const 1 + call $~lib/array/Array#__get + i32.const -1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 200 + i32.const 15 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + local.get $2 + call $~lib/rt/stub/__release + ) + (func $start (; 15 ;) + call $start:infer-array + ) +) From b9dea7748aeb900fa7ecfb4fdcf588fc2d41e244 Mon Sep 17 00:00:00 2001 From: dcode Date: Sat, 21 Dec 2019 14:38:30 +0100 Subject: [PATCH 2/4] determine common denominator --- src/compiler.ts | 55 +++++--- tests/compiler/infer-array.optimized.wat | 69 +++++++++- tests/compiler/infer-array.ts | 24 +++- tests/compiler/infer-array.untouched.wat | 162 ++++++++++++++++++++++- 4 files changed, 283 insertions(+), 27 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 349bd4d81e..b586f4c1a4 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7459,22 +7459,16 @@ export class Compiler extends DiagnosticEmitter { // Infer from first element in auto contexts if (contextualType == Type.auto) { - if (elementExpressions.length) { - let first = elementExpressions[0]; - if (first) { - let firstType = this.resolver.resolveExpression(first, this.currentFlow); - if (!firstType) return module.unreachable(); - return this.compileArrayLiteral( - firstType, - elementExpressions, - constraints, - expression - ); - } - } + return this.compileArrayLiteral( + Type.auto, + elementExpressions, + constraints, + expression + ); + } // Use contextual type if an array - } else if (contextualType.is(TypeFlags.REFERENCE)) { + if (contextualType.is(TypeFlags.REFERENCE)) { let classType = contextualType.classReference; if (classType) { if (classType.prototype == this.program.arrayPrototype) { @@ -7566,17 +7560,42 @@ export class Compiler extends DiagnosticEmitter { var module = this.module; var program = this.program; var arrayPrototype = assert(program.arrayPrototype); - var arrayInstance = assert(this.resolver.resolveClass(arrayPrototype, [ elementType ])); var arrayBufferInstance = assert(program.arrayBufferInstance); - var arrayType = arrayInstance.type; var flow = this.currentFlow; // block those here so compiling expressions doesn't conflict - var tempThis = flow.getTempLocal(arrayType); + var tempThis = flow.getTempLocal(this.options.usizeType); var tempDataStart = flow.getTempLocal(arrayBufferInstance.type); - // compile value expressions and find out whether all are constant + // infer common element type in auto contexts var length = expressions.length; + if (elementType == Type.auto) { + for (let i = 0; i < length; ++i) { + let expression = expressions[i]; + if (expression) { + let currentType = this.resolver.resolveExpression(expression, this.currentFlow, elementType); + if (!currentType) return module.unreachable(); + if (elementType == Type.auto) elementType = currentType; + else { + let commonType = Type.commonDenominator(elementType, currentType, false); + if (commonType) elementType = commonType; + // otherwise triggers error further down + } + } + } + if (elementType /* still */ == Type.auto) { + this.error( + DiagnosticCode.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly, + reportNode.range, "T" + ); + return module.unreachable(); + } + } + + var arrayInstance = assert(this.resolver.resolveClass(arrayPrototype, [ elementType ])); + var arrayType = arrayInstance.type; + + // compile value expressions and find out whether all are constant var values = new Array(length); var isStatic = true; var nativeElementType = elementType.toNativeType(); diff --git a/tests/compiler/infer-array.optimized.wat b/tests/compiler/infer-array.optimized.wat index 8b127e5806..7eba7accff 100644 --- a/tests/compiler/infer-array.optimized.wat +++ b/tests/compiler/infer-array.optimized.wat @@ -3,6 +3,7 @@ (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $none_=>_i32 (func (result i32))) (type $i32_=>_i32 (func (param i32) (result i32))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) @@ -14,6 +15,8 @@ (data (i32.const 144) "\18\00\00\00\01\00\00\00\00\00\00\00\18") (data (i32.const 166) "\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") (data (i32.const 184) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s") + (data (i32.const 232) "\18\00\00\00\01\00\00\00\00\00\00\00\18") + (data (i32.const 254) "\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (export "memory" (memory $0)) @@ -331,12 +334,18 @@ i32.add i32.load ) - (func $start:infer-array (; 6 ;) + (func $infer-array/Ref#constructor (; 6 ;) (result i32) + i32.const 0 + i32.const 6 + call $~lib/rt/stub/__alloc + ) + (func $start:infer-array (; 7 ;) (local $0 i32) (local $1 i32) - i32.const 240 + (local $2 i32) + i32.const 272 global.set $~lib/rt/stub/startOffset - i32.const 240 + i32.const 272 global.set $~lib/rt/stub/offset i32.const 3 i32.const 2 @@ -375,8 +384,60 @@ call $~lib/builtins/abort unreachable end + i32.const 3 + i32.const 3 + i32.const 4 + i32.const 248 + call $~lib/rt/__allocArray + drop + call $infer-array/Ref#constructor + local.set $0 + call $infer-array/Ref#constructor + local.set $1 + i32.const 2 + i32.const 2 + i32.const 7 + i32.const 0 + call $~lib/rt/__allocArray + i32.load offset=4 + local.tee $2 + local.get $0 + i32.store + local.get $2 + local.get $1 + i32.store offset=4 + call $infer-array/Ref#constructor + local.set $0 + call $infer-array/Ref#constructor + local.set $1 + i32.const 2 + i32.const 2 + i32.const 7 + i32.const 0 + call $~lib/rt/__allocArray + i32.load offset=4 + local.tee $2 + local.get $0 + i32.store + local.get $2 + local.get $1 + i32.store offset=4 + call $infer-array/Ref#constructor + local.set $0 + i32.const 2 + i32.const 2 + i32.const 7 + i32.const 0 + call $~lib/rt/__allocArray + i32.load offset=4 + local.tee $1 + local.get $0 + i32.store + local.get $1 + i32.const 0 + i32.store offset=4 ) - (func $start (; 7 ;) + (func $start (; 8 ;) call $start:infer-array ) ) diff --git a/tests/compiler/infer-array.ts b/tests/compiler/infer-array.ts index 497ec2da97..221814ed93 100644 --- a/tests/compiler/infer-array.ts +++ b/tests/compiler/infer-array.ts @@ -9,8 +9,30 @@ } { let a: u32 = 0; - let arr = [ a, -1 ]; + let arr = [a, -1]; assert(isInteger(arr[0])); assert(!isSigned(arr[0])); assert(arr[1] == 4294967295); } +{ + let arr = [1, 2, 3.0]; + assert(isFloat(arr[0])); +} +class Ref {} +{ + let a: Ref = new Ref(); + let b: Ref | null = new Ref(); + let arr = [a, b]; + assert(isNullable(arr[0])); +} +{ + let a: Ref | null = new Ref(); + let b: Ref = new Ref(); + let arr = [a, b]; + assert(isNullable(arr[1])); +} +{ + let a: Ref = new Ref(); + let arr = [a, null]; + assert(isNullable(arr[0])); +} diff --git a/tests/compiler/infer-array.untouched.wat b/tests/compiler/infer-array.untouched.wat index f27a1c4278..39f70bd6ff 100644 --- a/tests/compiler/infer-array.untouched.wat +++ b/tests/compiler/infer-array.untouched.wat @@ -3,9 +3,9 @@ (type $none_=>_none (func)) (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) - (type $i32_=>_i32 (func (param i32) (result i32))) (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) @@ -14,11 +14,12 @@ (data (i32.const 96) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00") (data (i32.const 144) "\18\00\00\00\01\00\00\00\00\00\00\00\18\00\00\00\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") (data (i32.const 184) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s\00") + (data (i32.const 232) "\18\00\00\00\01\00\00\00\00\00\00\00\18\00\00\00\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") (table $0 1 funcref) (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (global $~lib/ASC_SHRINK_LEVEL i32 (i32.const 0)) - (global $~lib/heap/__heap_base i32 (i32.const 228)) + (global $~lib/heap/__heap_base i32 (i32.const 272)) (export "memory" (memory $0)) (start $start) (func $~lib/rt/stub/maybeGrowMemory (; 1 ;) (param $0 i32) @@ -1512,10 +1513,53 @@ local.set $2 local.get $2 ) - (func $start:infer-array (; 14 ;) + (func $infer-array/Ref#constructor (; 14 ;) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 6 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $~lib/array/Array#__unchecked_get (; 15 ;) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 2 + i32.shl + i32.add + i32.load + call $~lib/rt/stub/__retain + ) + (func $~lib/array/Array#__get (; 16 ;) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/array/Array#__unchecked_get + local.set $2 + local.get $2 + ) + (func $start:infer-array (; 17 ;) (local $0 i32) (local $1 i32) (local $2 i32) + (local $3 i32) global.get $~lib/heap/__heap_base i32.const 15 i32.add @@ -1588,8 +1632,118 @@ end local.get $2 call $~lib/rt/stub/__release + i32.const 3 + i32.const 3 + i32.const 4 + i32.const 248 + call $~lib/rt/__allocArray + call $~lib/rt/stub/__retain + local.tee $0 + call $~lib/rt/stub/__retain + local.set $2 + local.get $0 + call $~lib/rt/stub/__release + local.get $2 + call $~lib/rt/stub/__release + i32.const 0 + call $infer-array/Ref#constructor + local.set $2 + i32.const 0 + call $infer-array/Ref#constructor + local.set $0 + i32.const 2 + i32.const 2 + i32.const 7 + i32.const 0 + call $~lib/rt/__allocArray + local.set $1 + local.get $1 + i32.load offset=4 + local.set $3 + local.get $3 + local.get $2 + call $~lib/rt/stub/__retain + i32.store + local.get $3 + local.get $0 + call $~lib/rt/stub/__retain + i32.store offset=4 + local.get $1 + call $~lib/rt/stub/__retain + local.set $3 + local.get $2 + call $~lib/rt/stub/__release + local.get $0 + call $~lib/rt/stub/__release + local.get $3 + call $~lib/rt/stub/__release + local.get $1 + call $~lib/rt/stub/__release + i32.const 0 + call $infer-array/Ref#constructor + local.set $1 + i32.const 0 + call $infer-array/Ref#constructor + local.set $3 + i32.const 2 + i32.const 2 + i32.const 7 + i32.const 0 + call $~lib/rt/__allocArray + local.set $0 + local.get $0 + i32.load offset=4 + local.set $2 + local.get $2 + local.get $1 + call $~lib/rt/stub/__retain + i32.store + local.get $2 + local.get $3 + call $~lib/rt/stub/__retain + i32.store offset=4 + local.get $0 + call $~lib/rt/stub/__retain + local.set $2 + local.get $1 + call $~lib/rt/stub/__release + local.get $3 + call $~lib/rt/stub/__release + local.get $2 + call $~lib/rt/stub/__release + local.get $0 + call $~lib/rt/stub/__release + i32.const 0 + call $infer-array/Ref#constructor + local.set $0 + i32.const 2 + i32.const 2 + i32.const 7 + i32.const 0 + call $~lib/rt/__allocArray + local.set $2 + local.get $2 + i32.load offset=4 + local.set $3 + local.get $3 + local.get $0 + call $~lib/rt/stub/__retain + i32.store + local.get $3 + i32.const 0 + call $~lib/rt/stub/__retain + i32.store offset=4 + local.get $2 + call $~lib/rt/stub/__retain + local.set $3 + local.get $0 + call $~lib/rt/stub/__release + local.get $3 + call $~lib/rt/stub/__release + local.get $2 + call $~lib/rt/stub/__release ) - (func $start (; 15 ;) + (func $start (; 18 ;) call $start:infer-array ) ) From 91d4c356968be01abaa9e1a18e4e3f8c92cb81c1 Mon Sep 17 00:00:00 2001 From: dcode Date: Sat, 21 Dec 2019 17:10:39 +0100 Subject: [PATCH 3/4] add fast path --- src/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index b586f4c1a4..f2a4b8ad05 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7576,7 +7576,7 @@ export class Compiler extends DiagnosticEmitter { let currentType = this.resolver.resolveExpression(expression, this.currentFlow, elementType); if (!currentType) return module.unreachable(); if (elementType == Type.auto) elementType = currentType; - else { + else if (currentType != elementType) { let commonType = Type.commonDenominator(elementType, currentType, false); if (commonType) elementType = commonType; // otherwise triggers error further down From 3cc3bb438073e925894e6e928968f591c413527c Mon Sep 17 00:00:00 2001 From: dcode Date: Sun, 22 Dec 2019 19:22:25 +0100 Subject: [PATCH 4/4] more tests --- tests/compiler/infer-array.optimized.wat | 69 +++++--- tests/compiler/infer-array.ts | 22 +-- tests/compiler/infer-array.untouched.wat | 191 ++++++++++++++--------- 3 files changed, 173 insertions(+), 109 deletions(-) diff --git a/tests/compiler/infer-array.optimized.wat b/tests/compiler/infer-array.optimized.wat index 7eba7accff..9a955b40d9 100644 --- a/tests/compiler/infer-array.optimized.wat +++ b/tests/compiler/infer-array.optimized.wat @@ -14,9 +14,11 @@ (data (i32.const 96) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s") (data (i32.const 144) "\18\00\00\00\01\00\00\00\00\00\00\00\18") (data (i32.const 166) "\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") - (data (i32.const 184) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s") - (data (i32.const 232) "\18\00\00\00\01\00\00\00\00\00\00\00\18") - (data (i32.const 254) "\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") + (data (i32.const 184) "\08\00\00\00\01\00\00\00\00\00\00\00\08\00\00\00\01\00\00\00\ff\ff\ff\ff") + (data (i32.const 208) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s") + (data (i32.const 256) "\18\00\00\00\01\00\00\00\00\00\00\00\18") + (data (i32.const 278) "\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") + (data (i32.const 296) "\0c\00\00\00\01\00\00\00\00\00\00\00\0c\00\00\00\00\00\80?\00\00\00@\00\00@@") (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (export "memory" (memory $0)) @@ -334,18 +336,38 @@ i32.add i32.load ) - (func $infer-array/Ref#constructor (; 6 ;) (result i32) + (func $~lib/array/Array#__get (; 6 ;) (param $0 i32) + i32.const 1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + i32.const 4 + i32.add + f32.load + drop + ) + (func $infer-array/Ref#constructor (; 7 ;) (result i32) i32.const 0 - i32.const 6 + i32.const 7 call $~lib/rt/stub/__alloc ) - (func $start:infer-array (; 7 ;) + (func $start:infer-array (; 8 ;) (local $0 i32) (local $1 i32) (local $2 i32) - i32.const 272 + i32.const 336 global.set $~lib/rt/stub/startOffset - i32.const 272 + i32.const 336 global.set $~lib/rt/stub/offset i32.const 3 i32.const 2 @@ -362,24 +384,15 @@ i32.const 2 i32.const 2 i32.const 5 - i32.const 0 + i32.const 200 call $~lib/rt/__allocArray - local.tee $0 - i32.load offset=4 - local.tee $1 - i32.const 0 - i32.store - local.get $1 - i32.const -1 - i32.store offset=4 - local.get $0 call $~lib/array/Array#__get i32.const -1 i32.ne if i32.const 0 - i32.const 200 - i32.const 15 + i32.const 224 + i32.const 14 i32.const 2 call $~lib/builtins/abort unreachable @@ -387,16 +400,22 @@ i32.const 3 i32.const 3 i32.const 4 - i32.const 248 + i32.const 272 call $~lib/rt/__allocArray drop + i32.const 3 + i32.const 2 + i32.const 6 + i32.const 312 + call $~lib/rt/__allocArray + call $~lib/array/Array#__get call $infer-array/Ref#constructor local.set $0 call $infer-array/Ref#constructor local.set $1 i32.const 2 i32.const 2 - i32.const 7 + i32.const 8 i32.const 0 call $~lib/rt/__allocArray i32.load offset=4 @@ -412,7 +431,7 @@ local.set $1 i32.const 2 i32.const 2 - i32.const 7 + i32.const 8 i32.const 0 call $~lib/rt/__allocArray i32.load offset=4 @@ -426,7 +445,7 @@ local.set $0 i32.const 2 i32.const 2 - i32.const 7 + i32.const 8 i32.const 0 call $~lib/rt/__allocArray i32.load offset=4 @@ -437,7 +456,7 @@ i32.const 0 i32.store offset=4 ) - (func $start (; 8 ;) + (func $start (; 9 ;) call $start:infer-array ) ) diff --git a/tests/compiler/infer-array.ts b/tests/compiler/infer-array.ts index 221814ed93..130459afdb 100644 --- a/tests/compiler/infer-array.ts +++ b/tests/compiler/infer-array.ts @@ -1,37 +1,41 @@ -{ +{ // default to i32 for integer literals let arr = [1, 2, 3]; assert(isInteger(arr[0])); assert(isSigned(arr[0])); } -{ +{ // default to f64 for float literals let arr = [1.0, 2, 3]; assert(isFloat(arr[0])); } -{ - let a: u32 = 0; - let arr = [a, -1]; +{ // retain signedness + let arr = [1, -1]; assert(isInteger(arr[0])); assert(!isSigned(arr[0])); assert(arr[1] == 4294967295); } -{ +{ // use common denominator let arr = [1, 2, 3.0]; assert(isFloat(arr[0])); } +{ // pass contextual type upwards + let arr = [1.0, 2.0, 3]; + assert(isFloat(arr[0])); + let f: f32 = arr[1]; // would error if f64 +} class Ref {} -{ +{ // use common nullable state let a: Ref = new Ref(); let b: Ref | null = new Ref(); let arr = [a, b]; assert(isNullable(arr[0])); } -{ +{ // allow lesser nullable state let a: Ref | null = new Ref(); let b: Ref = new Ref(); let arr = [a, b]; assert(isNullable(arr[1])); } -{ +{ // also works with null literal let a: Ref = new Ref(); let arr = [a, null]; assert(isNullable(arr[0])); diff --git a/tests/compiler/infer-array.untouched.wat b/tests/compiler/infer-array.untouched.wat index 39f70bd6ff..74fb2b4c45 100644 --- a/tests/compiler/infer-array.untouched.wat +++ b/tests/compiler/infer-array.untouched.wat @@ -4,6 +4,7 @@ (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_=>_f32 (func (param i32 i32) (result f32))) (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) @@ -13,13 +14,15 @@ (data (i32.const 40) "$\00\00\00\01\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00") (data (i32.const 96) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00") (data (i32.const 144) "\18\00\00\00\01\00\00\00\00\00\00\00\18\00\00\00\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") - (data (i32.const 184) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s\00") - (data (i32.const 232) "\18\00\00\00\01\00\00\00\00\00\00\00\18\00\00\00\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") + (data (i32.const 184) "\08\00\00\00\01\00\00\00\00\00\00\00\08\00\00\00\01\00\00\00\ff\ff\ff\ff") + (data (i32.const 208) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00i\00n\00f\00e\00r\00-\00a\00r\00r\00a\00y\00.\00t\00s\00") + (data (i32.const 256) "\18\00\00\00\01\00\00\00\00\00\00\00\18\00\00\00\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00@\00\00\00\00\00\00\08@") + (data (i32.const 296) "\0c\00\00\00\01\00\00\00\00\00\00\00\0c\00\00\00\00\00\80?\00\00\00@\00\00@@") (table $0 1 funcref) (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (global $~lib/ASC_SHRINK_LEVEL i32 (i32.const 0)) - (global $~lib/heap/__heap_base i32 (i32.const 272)) + (global $~lib/heap/__heap_base i32 (i32.const 324)) (export "memory" (memory $0)) (start $start) (func $~lib/rt/stub/maybeGrowMemory (; 1 ;) (param $0 i32) @@ -1513,19 +1516,48 @@ local.set $2 local.get $2 ) - (func $infer-array/Ref#constructor (; 14 ;) (param $0 i32) (result i32) + (func $~lib/array/Array#__unchecked_get (; 14 ;) (param $0 i32) (param $1 i32) (result f32) + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 2 + i32.shl + i32.add + f32.load + ) + (func $~lib/array/Array#__get (; 15 ;) (param $0 i32) (param $1 i32) (result f32) + (local $2 f32) + local.get $1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 56 + i32.const 112 + i32.const 93 + i32.const 41 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/array/Array#__unchecked_get + local.set $2 + local.get $2 + ) + (func $infer-array/Ref#constructor (; 16 ;) (param $0 i32) (result i32) local.get $0 i32.eqz if i32.const 0 - i32.const 6 + i32.const 7 call $~lib/rt/stub/__alloc call $~lib/rt/stub/__retain local.set $0 end local.get $0 ) - (func $~lib/array/Array#__unchecked_get (; 15 ;) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/array/Array#__unchecked_get (; 17 ;) (param $0 i32) (param $1 i32) (result i32) local.get $0 i32.load offset=4 local.get $1 @@ -1535,7 +1567,7 @@ i32.load call $~lib/rt/stub/__retain ) - (func $~lib/array/Array#__get (; 16 ;) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/array/Array#__get (; 18 ;) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) local.get $1 local.get $0 @@ -1555,11 +1587,12 @@ local.set $2 local.get $2 ) - (func $start:infer-array (; 17 ;) + (func $start:infer-array (; 19 ;) (local $0 i32) (local $1 i32) - (local $2 i32) + (local $2 f32) (local $3 i32) + (local $4 i32) global.get $~lib/heap/__heap_base i32.const 15 i32.add @@ -1596,27 +1629,16 @@ call $~lib/rt/stub/__release local.get $0 call $~lib/rt/stub/__release - i32.const 0 - local.set $0 i32.const 2 i32.const 2 i32.const 5 - i32.const 0 + i32.const 200 call $~lib/rt/__allocArray - local.set $1 - local.get $1 - i32.load offset=4 - local.set $2 - local.get $2 - local.get $0 - i32.store - local.get $2 - i32.const -1 - i32.store offset=4 - local.get $1 call $~lib/rt/stub/__retain - local.set $2 - local.get $2 + local.tee $1 + call $~lib/rt/stub/__retain + local.set $0 + local.get $0 i32.const 1 call $~lib/array/Array#__get i32.const -1 @@ -1624,126 +1646,145 @@ i32.eqz if i32.const 0 - i32.const 200 - i32.const 15 + i32.const 224 + i32.const 14 i32.const 2 call $~lib/builtins/abort unreachable end - local.get $2 + local.get $1 + call $~lib/rt/stub/__release + local.get $0 call $~lib/rt/stub/__release i32.const 3 i32.const 3 i32.const 4 - i32.const 248 + i32.const 272 call $~lib/rt/__allocArray call $~lib/rt/stub/__retain - local.tee $0 + local.tee $1 call $~lib/rt/stub/__retain - local.set $2 + local.set $0 + local.get $1 + call $~lib/rt/stub/__release local.get $0 call $~lib/rt/stub/__release - local.get $2 + i32.const 3 + i32.const 2 + i32.const 6 + i32.const 312 + call $~lib/rt/__allocArray + call $~lib/rt/stub/__retain + local.tee $1 + call $~lib/rt/stub/__retain + local.set $0 + local.get $0 + i32.const 1 + call $~lib/array/Array#__get + local.set $2 + local.get $1 + call $~lib/rt/stub/__release + local.get $0 call $~lib/rt/stub/__release i32.const 0 call $infer-array/Ref#constructor - local.set $2 + local.set $0 i32.const 0 call $infer-array/Ref#constructor - local.set $0 + local.set $1 i32.const 2 i32.const 2 - i32.const 7 + i32.const 8 i32.const 0 call $~lib/rt/__allocArray - local.set $1 - local.get $1 - i32.load offset=4 local.set $3 local.get $3 - local.get $2 + i32.load offset=4 + local.set $4 + local.get $4 + local.get $0 call $~lib/rt/stub/__retain i32.store - local.get $3 - local.get $0 + local.get $4 + local.get $1 call $~lib/rt/stub/__retain i32.store offset=4 - local.get $1 + local.get $3 call $~lib/rt/stub/__retain - local.set $3 - local.get $2 - call $~lib/rt/stub/__release + local.set $4 local.get $0 call $~lib/rt/stub/__release - local.get $3 - call $~lib/rt/stub/__release local.get $1 call $~lib/rt/stub/__release + local.get $4 + call $~lib/rt/stub/__release + local.get $3 + call $~lib/rt/stub/__release i32.const 0 call $infer-array/Ref#constructor - local.set $1 + local.set $3 i32.const 0 call $infer-array/Ref#constructor - local.set $3 + local.set $4 i32.const 2 i32.const 2 - i32.const 7 + i32.const 8 i32.const 0 call $~lib/rt/__allocArray + local.set $1 + local.get $1 + i32.load offset=4 local.set $0 local.get $0 - i32.load offset=4 - local.set $2 - local.get $2 - local.get $1 - call $~lib/rt/stub/__retain - i32.store - local.get $2 local.get $3 call $~lib/rt/stub/__retain - i32.store offset=4 + i32.store local.get $0 + local.get $4 call $~lib/rt/stub/__retain - local.set $2 + i32.store offset=4 local.get $1 - call $~lib/rt/stub/__release + call $~lib/rt/stub/__retain + local.set $0 local.get $3 call $~lib/rt/stub/__release - local.get $2 + local.get $4 call $~lib/rt/stub/__release local.get $0 call $~lib/rt/stub/__release + local.get $1 + call $~lib/rt/stub/__release i32.const 0 call $infer-array/Ref#constructor - local.set $0 + local.set $1 i32.const 2 i32.const 2 - i32.const 7 + i32.const 8 i32.const 0 call $~lib/rt/__allocArray - local.set $2 - local.get $2 - i32.load offset=4 - local.set $3 - local.get $3 + local.set $0 local.get $0 + i32.load offset=4 + local.set $4 + local.get $4 + local.get $1 call $~lib/rt/stub/__retain i32.store - local.get $3 + local.get $4 i32.const 0 call $~lib/rt/stub/__retain i32.store offset=4 - local.get $2 - call $~lib/rt/stub/__retain - local.set $3 local.get $0 + call $~lib/rt/stub/__retain + local.set $4 + local.get $1 call $~lib/rt/stub/__release - local.get $3 + local.get $4 call $~lib/rt/stub/__release - local.get $2 + local.get $0 call $~lib/rt/stub/__release ) - (func $start (; 18 ;) + (func $start (; 20 ;) call $start:infer-array ) )