diff --git a/src/compiler.ts b/src/compiler.ts index b637192733..6dded2c241 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -3779,7 +3779,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, true); + commonType = Type.commonType(leftType, rightType, contextualType, true); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -3814,7 +3814,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, true); + commonType = Type.commonType(leftType, rightType, contextualType, true); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -3849,7 +3849,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, true); + commonType = Type.commonType(leftType, rightType, contextualType, true); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -3884,7 +3884,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, true); + commonType = Type.commonType(leftType, rightType, contextualType, true); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -3921,7 +3921,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -3973,7 +3973,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4038,7 +4038,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4083,7 +4083,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !leftType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4128,7 +4128,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4173,7 +4173,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4218,7 +4218,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4263,7 +4263,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isNumericValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4390,7 +4390,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isIntegerValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4435,7 +4435,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isIntegerValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4480,7 +4480,7 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType); rightType = this.currentType; - commonType = Type.commonDenominator(leftType, rightType, false); + commonType = Type.commonType(leftType, rightType, contextualType); if (!commonType || !commonType.isIntegerValue) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, @@ -4537,8 +4537,21 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.bool; } else { - rightExpr = this.compileExpression(right, leftType, inheritedConstraints | Constraints.ConvImplicit); + rightExpr = this.compileExpression(right, leftType, inheritedConstraints); rightType = this.currentType; + commonType = Type.commonType(leftType, rightType, contextualType); + if (!commonType) { + this.error( + DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, + expression.range, "&&", leftType.toString(), rightType.toString() + ); + this.currentType = contextualType; + return module.unreachable(); + } + leftExpr = this.convertExpression(leftExpr, leftType, commonType, false, left); + leftType = commonType; + rightExpr = this.convertExpression(rightExpr, rightType, commonType, false, right); + rightType = commonType; // simplify if copying left is trivial if (expr = module.tryCopyTrivialExpression(leftExpr)) { @@ -4562,7 +4575,7 @@ export class Compiler extends DiagnosticEmitter { flow.mergeBranch(rightFlow); // LHS && RHS -> RHS conditionally executes flow.noteThen(expr, rightFlow); // LHS && RHS == true -> RHS always executes this.currentFlow = flow; - this.currentType = leftType; + this.currentType = commonType; } break; } @@ -4603,8 +4616,22 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.bool; } else { - rightExpr = this.compileExpression(right, leftType, inheritedConstraints | Constraints.ConvImplicit); + rightExpr = this.compileExpression(right, leftType, inheritedConstraints); rightType = this.currentType; + commonType = Type.commonType(leftType, rightType, contextualType); + if (!commonType) { + this.error( + DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, + expression.range, "||", leftType.toString(), rightType.toString() + ); + this.currentType = contextualType; + return module.unreachable(); + } + let possiblyNull = leftType.is(TypeFlags.Nullable) && rightType.is(TypeFlags.Nullable); + leftExpr = this.convertExpression(leftExpr, leftType, commonType, false, left); + leftType = commonType; + rightExpr = this.convertExpression(rightExpr, rightType, commonType, false, right); + rightType = commonType; // simplify if copying left is trivial if (expr = module.tryCopyTrivialExpression(leftExpr)) { @@ -4629,7 +4656,9 @@ export class Compiler extends DiagnosticEmitter { flow.mergeBranch(rightFlow); // LHS || RHS -> RHS conditionally executes flow.noteElse(expr, rightFlow); // LHS || RHS == false -> RHS always executes this.currentFlow = flow; - this.currentType = leftType; + this.currentType = possiblyNull + ? commonType + : commonType.nonNullableType; } break; } @@ -8945,7 +8974,7 @@ export class Compiler extends DiagnosticEmitter { private compileTernaryExpression( expression: TernaryExpression, - ctxType: Type, + contextualType: Type, constraints: Constraints ): ExpressionRef { let module = this.module; @@ -8958,24 +8987,24 @@ export class Compiler extends DiagnosticEmitter { // FIXME: skips common denominator, inconsistently picking branch type let condKind = this.evaluateCondition(condExprTrueish); if (condKind == ConditionKind.True) { - return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifThen, ctxType)); + return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifThen, contextualType)); } if (condKind == ConditionKind.False) { - return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifElse, ctxType)); + return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifElse, contextualType)); } let outerFlow = this.currentFlow; let ifThenFlow = outerFlow.forkThen(condExpr); this.currentFlow = ifThenFlow; - let ifThenExpr = this.compileExpression(ifThen, ctxType); + let ifThenExpr = this.compileExpression(ifThen, contextualType); let ifThenType = this.currentType; let ifElseFlow = outerFlow.forkElse(condExpr); this.currentFlow = ifElseFlow; - let ifElseExpr = this.compileExpression(ifElse, ctxType == Type.auto ? ifThenType : ctxType); + let ifElseExpr = this.compileExpression(ifElse, contextualType == Type.auto ? ifThenType : contextualType); let ifElseType = this.currentType; - if (ctxType == Type.void) { // values, including type mismatch, are irrelevant + if (contextualType == Type.void) { // values, including type mismatch, are irrelevant if (ifThenType != Type.void) { ifThenExpr = module.drop(ifThenExpr); ifThenType = Type.void; @@ -8986,13 +9015,13 @@ export class Compiler extends DiagnosticEmitter { } this.currentType = Type.void; } else { - let commonType = Type.commonDenominator(ifThenType, ifElseType, false); + let commonType = Type.commonType(ifThenType, ifElseType, contextualType); if (!commonType) { this.error( DiagnosticCode.Type_0_is_not_assignable_to_type_1, ifElse.range, ifElseType.toString(), ifThenType.toString() ); - this.currentType = ctxType; + this.currentType = contextualType; return module.unreachable(); } ifThenExpr = this.convertExpression(ifThenExpr, ifThenType, commonType, false, ifThen); diff --git a/src/module.ts b/src/module.ts index 20c05e8130..cad042b46b 100644 --- a/src/module.ts +++ b/src/module.ts @@ -128,6 +128,46 @@ export namespace HeapTypeRef { export function isSubtype(ht: HeapTypeRef, superHt: HeapTypeRef): bool { return binaryen._BinaryenHeapTypeIsSubType(ht, superHt); } + + export function leastUpperBound(a: HeapTypeRef, b: HeapTypeRef): HeapTypeRef { + // see binaryen/src/wasm/wasm-type.cpp + if (a == b) return a; + if (getBottom(a) != getBottom(b)) return -1; + if (isBottom(a)) return b; + if (isBottom(b)) return a; + if (a > b) { + let t = a; + a = b; + b = t; + } + switch (a) { + case HeapTypeRef.Extern: + case HeapTypeRef.Func: return -1; + case HeapTypeRef.Any: return a; + case HeapTypeRef.Eq: { + return b == HeapTypeRef.I31 || b == HeapTypeRef.Data || b == HeapTypeRef.Array + ? HeapTypeRef.Eq + : HeapTypeRef.Any; + } + case HeapTypeRef.I31: { + return b == HeapTypeRef.Data || b == HeapTypeRef.Array + ? HeapTypeRef.Eq + : HeapTypeRef.Any; + } + case HeapTypeRef.Data: { + return b == HeapTypeRef.Array + ? HeapTypeRef.Data + : HeapTypeRef.Any; + } + case HeapTypeRef.Array: + case HeapTypeRef.String: + case HeapTypeRef.StringviewWTF8: + case HeapTypeRef.StringviewWTF16: + case HeapTypeRef.StringviewIter: return HeapTypeRef.Any; + } + assert(false); + return -1; + } } /** Packed array element respectively struct field types. */ diff --git a/src/program.ts b/src/program.ts index 7ca4cab9f4..ba14333779 100644 --- a/src/program.ts +++ b/src/program.ts @@ -4415,6 +4415,27 @@ export class Class extends TypedElement { registerConcreteElement(program, this); } + /** Computes the least upper bound of two class types. */ + static leastUpperBound(a: Class, b: Class): Class | null { + if (a == b) return a; + let candidates = new Set(); + candidates.add(a); + candidates.add(b); + while (true) { + let aBase = a.base; + let bBase = b.base; + if (!aBase && !bBase) return null; // none + if (aBase) { + if (candidates.has(aBase)) return aBase; + candidates.add(a = aBase); + } + if (bBase) { + if (candidates.has(bBase)) return bBase; + candidates.add(b = bBase); + } + } + } + /** Sets the base class. */ setBase(base: Class): void { assert(!this.base); diff --git a/src/resolver.ts b/src/resolver.ts index 20005b954d..a3abb9d70e 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -83,7 +83,8 @@ import { Type, Signature, typesToString, - TypeKind + TypeKind, + TypeFlags } from "./types"; import { @@ -2070,12 +2071,12 @@ export class Resolver extends DiagnosticEmitter { } let rightType = this.resolveExpression(right, ctxFlow, leftType, reportMode); if (!rightType) return null; - let commonType = Type.commonDenominator(leftType, rightType, false); + let commonType = Type.commonType(leftType, rightType, ctxType); if (!commonType) { if (reportMode == ReportMode.Report) { this.error( DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, - node.range, leftType.toString(), rightType.toString() + node.range, operatorTokenToString(operator), leftType.toString(), rightType.toString() ); } } @@ -2120,7 +2121,7 @@ export class Resolver extends DiagnosticEmitter { } let rightType = this.resolveExpression(right, ctxFlow, ctxType, reportMode); if (!rightType) return null; - let commonType = Type.commonDenominator(leftType, rightType, false); + let commonType = Type.commonType(leftType, rightType, ctxType); if (!commonType || !commonType.isIntegerValue) { if (reportMode == ReportMode.Report) { this.error( @@ -2132,11 +2133,43 @@ export class Resolver extends DiagnosticEmitter { return commonType; } - // logical: result is LHS (RHS is converted to LHS), not supporting overloads + // logical - case Token.Ampersand_Ampersand: + case Token.Ampersand_Ampersand: { + let leftType = this.resolveExpression(left, ctxFlow, ctxType, reportMode); + if (!leftType) return null; + let rightType = this.resolveExpression(right, ctxFlow, leftType, reportMode); + if (!rightType) return null; + let commonType = Type.commonType(leftType, rightType, ctxType); + if (!commonType) { + if (reportMode == ReportMode.Report) { + this.error( + DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, + node.range, "&&", leftType.toString(), rightType.toString() + ); + } + } + return commonType; + } case Token.Bar_Bar: { - return this.resolveExpression(left, ctxFlow, ctxType, reportMode); + let leftType = this.resolveExpression(left, ctxFlow, ctxType, reportMode); + if (!leftType) return null; + let rightType = this.resolveExpression(right, ctxFlow, leftType, reportMode); + if (!rightType) return null; + let commonType = Type.commonType(leftType, rightType, ctxType); + if (!commonType) { + if (reportMode == ReportMode.Report) { + this.error( + DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2, + node.range, "||", leftType.toString(), rightType.toString() + ); + } + return null; + } + // `LHS || RHS` can only be null if both LHS and RHS are null + return leftType.is(TypeFlags.Nullable) && rightType.is(TypeFlags.Nullable) + ? commonType + : commonType.nonNullableType; } } assert(false); @@ -2317,7 +2350,7 @@ export class Resolver extends DiagnosticEmitter { if (!currentType) return null; if (elementType == Type.auto) elementType = currentType; else if (currentType != elementType) { - let commonType = Type.commonDenominator(elementType, currentType, false); + let commonType = Type.commonType(elementType, currentType, elementType); if (commonType) elementType = commonType; // otherwise triggers error on compilation } @@ -2574,7 +2607,7 @@ export class Resolver extends DiagnosticEmitter { if (!thenType) return null; let elseType = this.resolveExpression(node.ifElse, ctxFlow, thenType, reportMode); if (!elseType) return null; - let commonType = Type.commonDenominator(thenType, elseType, false); + let commonType = Type.commonType(thenType, elseType, ctxType); if (!commonType) { if (reportMode == ReportMode.Report) { this.error( diff --git a/src/types.ts b/src/types.ts index 5e22db7ce3..acdf170ce8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -65,12 +65,12 @@ export const enum TypeKind { /** A 128-bit vector. */ V128, - // references + // references (keep in same order as in Binaryen) - /** Function reference. */ - Funcref, /** External reference. */ Externref, + /** Function reference. */ + Funcref, /** Any reference. */ Anyref, /** Equatable reference. */ @@ -546,10 +546,40 @@ export class Type { return true; } - /** Determines the common denominator type of two types, if there is any. */ - static commonDenominator(left: Type, right: Type, signednessIsImportant: bool): Type | null { - if (right.isAssignableTo(left, signednessIsImportant)) return left; - else if (left.isAssignableTo(right, signednessIsImportant)) return right; + /** Computes the common type of a binary-like expression, if any. */ + static commonType( + /** LHS type. */ + left: Type, + /** RHS type. */ + right: Type, + /** Contextual type, if any. */ + contextualType: Type = Type.auto, + /** Whether signedness is relevant. */ + signednessIsRelevant: bool = false + ): Type | null { + // Compute LUB of internal reference types (classes) + if (left.isInternalReference) { + if (!right.isInternalReference) return null; + // Prefer contextual type if meaningful + if (contextualType != Type.void && left.isAssignableTo(contextualType) && right.isAssignableTo(contextualType)) { + return contextualType; + } + let leftClass = left.getClass(); + let rightClass = right.getClass(); + if (leftClass && rightClass) { + let lubClass = Class.leastUpperBound(leftClass, rightClass); + if (lubClass) { + let ret = left.is(TypeFlags.Nullable) || right.is(TypeFlags.Nullable) ? lubClass.type.asNullable() : lubClass.type; + return ret; + } + } + } else if (right.isInternalReference) { + return null; + } + // TODO: External reference types (needs nullability) + // Otherwise do a trivial check + if (right.isAssignableTo(left, signednessIsRelevant)) return left; + else if (left.isAssignableTo(right, signednessIsRelevant)) return right; return null; } diff --git a/tests/compiler/logical.debug.wat b/tests/compiler/logical.debug.wat index ca79a86ba1..9c2b40df28 100644 --- a/tests/compiler/logical.debug.wat +++ b/tests/compiler/logical.debug.wat @@ -28,10 +28,12 @@ (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) + (global $logical/b (mut i32) (i32.const 0)) + (global $logical/c (mut i32) (i32.const 0)) (global $~lib/rt/__rtti_base i32 (i32.const 464)) - (global $~lib/memory/__data_end i32 (i32.const 488)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33256)) - (global $~lib/memory/__heap_base i32 (i32.const 33256)) + (global $~lib/memory/__data_end i32 (i32.const 500)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33268)) + (global $~lib/memory/__heap_base i32 (i32.const 33268)) (memory $0 1) (data (i32.const 12) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\14\00\00\00l\00o\00g\00i\00c\00a\00l\00.\00t\00s\00\00\00\00\00\00\00\00\00") (data (i32.const 60) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") @@ -42,7 +44,7 @@ (data (i32.const 316) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00") (data (i32.const 368) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 396) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 464) "\05\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00 \00\00\00") + (data (i32.const 464) "\08\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00 \00\00\00 \00\00\00 \00\00\00 \00\00\00") (table $0 1 1 funcref) (elem $0 (i32.const 1)) (export "memory" (memory $0)) @@ -2280,54 +2282,40 @@ end return ) - (func $~lib/rt/itcms/__collect (type $none_=>_none) - i32.const 0 - drop - global.get $~lib/rt/itcms/state - i32.const 0 - i32.gt_s - if - loop $while-continue|0 - global.get $~lib/rt/itcms/state - i32.const 0 - i32.ne - if - call $~lib/rt/itcms/step - drop - br $while-continue|0 - end - end + (func $logical/commonOr (type $i32_=>_i32) (param $b i32) (result i32) + local.get $b + if (result i32) + local.get $b + else + global.get $logical/c end - call $~lib/rt/itcms/step - drop - loop $while-continue|1 - global.get $~lib/rt/itcms/state - i32.const 0 - i32.ne - if - call $~lib/rt/itcms/step - drop - br $while-continue|1 - end + return + ) + (func $logical/commonAnd (type $i32_=>_i32) (param $b i32) (result i32) + local.get $b + if (result i32) + global.get $logical/c + else + local.get $b end - global.get $~lib/rt/itcms/total - i64.extend_i32_u - i32.const 200 - i64.extend_i32_u - i64.mul - i64.const 100 - i64.div_u - i32.wrap_i64 - i32.const 1024 - i32.add - global.set $~lib/rt/itcms/threshold - i32.const 0 - drop - i32.const 0 - drop + return ) (func $~lib/rt/__visit_globals (type $i32_=>_none) (param $0 i32) (local $1 i32) + global.get $logical/b + local.tee $1 + if + local.get $1 + local.get $0 + call $~lib/rt/itcms/__visit + end + global.get $logical/c + local.tee $1 + if + local.get $1 + local.get $0 + call $~lib/rt/itcms/__visit + end i32.const 272 local.get $0 call $~lib/rt/itcms/__visit @@ -2354,26 +2342,35 @@ ) (func $~lib/rt/__visit_members (type $i32_i32_=>_none) (param $0 i32) (param $1 i32) block $invalid - block $logical/Obj - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - block $~lib/object/Object - local.get $0 - i32.const 8 - i32.sub - i32.load $0 - br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $logical/Obj $invalid + block $logical/C + block $logical/A + block $logical/B + block $logical/Obj + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + block $~lib/object/Object + local.get $0 + i32.const 8 + i32.sub + i32.load $0 + br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $logical/Obj $logical/B $logical/A $logical/C $invalid + end + return + end + return + end + return end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit return end return end return end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit return end return @@ -2388,8 +2385,8 @@ global.get $~lib/memory/__data_end i32.lt_s if - i32.const 33280 - i32.const 33328 + i32.const 33296 + i32.const 33344 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -2434,6 +2431,120 @@ global.set $~lib/memory/__stack_pointer local.get $1 ) + (func $logical/A#constructor (type $i32_=>_i32) (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store $0 + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 6 + call $~lib/rt/itcms/__new + local.tee $this + i32.store $0 + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store $0 offset=4 + local.get $1 + call $~lib/object/Object#constructor + local.tee $this + i32.store $0 + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $logical/B#constructor (type $i32_=>_i32) (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store $0 + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 5 + call $~lib/rt/itcms/__new + local.tee $this + i32.store $0 + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store $0 offset=4 + local.get $1 + call $logical/A#constructor + local.tee $this + i32.store $0 + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $logical/C#constructor (type $i32_=>_i32) (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store $0 + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 7 + call $~lib/rt/itcms/__new + local.tee $this + i32.store $0 + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store $0 offset=4 + local.get $1 + call $logical/A#constructor + local.tee $this + i32.store $0 + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) (func $start:logical (type $none_=>_none) (local $0 f32) (local $1 f32) @@ -2979,9 +3090,74 @@ call $~lib/builtins/abort unreachable end - global.get $~lib/memory/__heap_base - global.set $~lib/memory/__stack_pointer - call $~lib/rt/itcms/__collect + i32.const 0 + call $logical/B#constructor + global.set $logical/b + i32.const 0 + call $logical/C#constructor + global.set $logical/c + global.get $logical/b + local.set $4 + global.get $~lib/memory/__stack_pointer + local.get $4 + i32.store $0 + local.get $4 + call $logical/commonOr + global.get $logical/b + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 106 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + call $logical/commonOr + global.get $logical/c + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 107 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $logical/b + local.set $4 + global.get $~lib/memory/__stack_pointer + local.get $4 + i32.store $0 + local.get $4 + call $logical/commonAnd + global.get $logical/c + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 112 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + call $logical/commonAnd + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 113 + i32.const 1 + call $~lib/builtins/abort + unreachable + end global.get $~lib/memory/__stack_pointer i32.const 4 i32.add diff --git a/tests/compiler/logical.json b/tests/compiler/logical.json index b83788465e..1bdd02b1be 100644 --- a/tests/compiler/logical.json +++ b/tests/compiler/logical.json @@ -1,5 +1,4 @@ { "asc_flags": [ - ], - "asc_rtrace": true + ] } diff --git a/tests/compiler/logical.release.wat b/tests/compiler/logical.release.wat index 9dcaebeb19..704df6e73f 100644 --- a/tests/compiler/logical.release.wat +++ b/tests/compiler/logical.release.wat @@ -1,9 +1,9 @@ (module + (type $i32_=>_i32 (func_subtype (param i32) (result i32) func)) (type $none_=>_none (func_subtype func)) (type $i32_=>_none (func_subtype (param i32) func)) (type $i32_i32_=>_none (func_subtype (param i32 i32) func)) (type $none_=>_i32 (func_subtype (result i32) func)) - (type $i32_=>_i32 (func_subtype (param i32) (result i32) func)) (type $i32_i32_i32_i32_=>_none (func_subtype (param i32 i32 i32 i32) func)) (type $i32_i32_i32_=>_none (func_subtype (param i32 i32 i32) func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) @@ -17,7 +17,9 @@ (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 34280)) + (global $logical/b (mut i32) (i32.const 0)) + (global $logical/c (mut i32) (i32.const 0)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 34292)) (memory $0 1) (data (i32.const 1036) ",") (data (i32.const 1048) "\02\00\00\00\14\00\00\00l\00o\00g\00i\00c\00a\00l\00.\00t\00s") @@ -31,12 +33,24 @@ (data (i32.const 1352) "\02\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s") (data (i32.const 1420) "<") (data (i32.const 1432) "\02\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") - (data (i32.const 1488) "\05\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1488) "\08\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00 \00\00\00 \00\00\00 \00\00\00 ") (export "memory" (memory $0)) (start $~start) (func $~lib/rt/itcms/visitRoots (type $none_=>_none) (local $0 i32) (local $1 i32) + global.get $logical/b + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + global.get $logical/c + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end i32.const 1296 call $byn-split-outlined-A$~lib/rt/itcms/__visit i32.const 1104 @@ -114,7 +128,7 @@ i32.load $0 offset=8 i32.eqz local.get $0 - i32.const 34280 + i32.const 34292 i32.lt_u i32.and i32.eqz @@ -742,10 +756,10 @@ if unreachable end - i32.const 34288 + i32.const 34304 i32.const 0 i32.store $0 - i32.const 35856 + i32.const 35872 i32.const 0 i32.store $0 loop $for-loop|0 @@ -756,7 +770,7 @@ local.get $0 i32.const 2 i32.shl - i32.const 34288 + i32.const 34304 i32.add i32.const 0 i32.store $0 offset=4 @@ -774,7 +788,7 @@ i32.add i32.const 2 i32.shl - i32.const 34288 + i32.const 34304 i32.add i32.const 0 i32.store $0 offset=96 @@ -792,13 +806,13 @@ br $for-loop|0 end end - i32.const 34288 - i32.const 35860 + i32.const 34304 + i32.const 35876 memory.size $0 i32.const 16 i32.shl call $~lib/rt/tlsf/addMemory - i32.const 34288 + i32.const 34304 global.set $~lib/rt/tlsf/ROOT ) (func $~lib/rt/itcms/step (type $none_=>_i32) (result i32) @@ -883,7 +897,7 @@ local.set $0 loop $while-continue|0 local.get $0 - i32.const 34280 + i32.const 34292 i32.lt_u if local.get $0 @@ -983,7 +997,7 @@ unreachable end local.get $0 - i32.const 34280 + i32.const 34292 i32.lt_u if local.get $0 @@ -1006,7 +1020,7 @@ i32.const 4 i32.add local.tee $0 - i32.const 34280 + i32.const 34292 i32.ge_u if global.get $~lib/rt/tlsf/ROOT @@ -1360,16 +1374,32 @@ ) (func $~lib/rt/__visit_members (type $i32_=>_none) (param $0 i32) block $invalid - block $logical/Obj - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - block $~lib/object/Object + block $logical/C + block $logical/A + block $logical/B + block $logical/Obj + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + block $~lib/object/Object + local.get $0 + i32.const 8 + i32.sub + i32.load $0 + br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $logical/Obj $logical/B $logical/A $logical/C $invalid + end + return + end + return + end + return + end + local.get $0 + i32.load $0 + local.tee $0 + if local.get $0 - i32.const 8 - i32.sub - i32.load $0 - br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $logical/Obj $invalid + call $byn-split-outlined-A$~lib/rt/itcms/__visit end return end @@ -1377,13 +1407,6 @@ end return end - local.get $0 - i32.load $0 - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end return end return @@ -1392,191 +1415,316 @@ ) (func $~start (type $none_=>_none) (local $0 i32) + (local $1 i32) + block $__inlined_func$start:logical + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1524 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store $0 + memory.size $0 + i32.const 16 + i32.shl + i32.const 34292 + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 1220 + i32.const 1216 + i32.store $0 + i32.const 1224 + i32.const 1216 + i32.store $0 + i32.const 1216 + global.set $~lib/rt/itcms/pinSpace + i32.const 1252 + i32.const 1248 + i32.store $0 + i32.const 1256 + i32.const 1248 + i32.store $0 + i32.const 1248 + global.set $~lib/rt/itcms/toSpace + i32.const 1396 + i32.const 1392 + i32.store $0 + i32.const 1400 + i32.const 1392 + i32.store $0 + i32.const 1392 + global.set $~lib/rt/itcms/fromSpace + call $logical/Obj#constructor + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store $0 + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 1056 + i32.const 87 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + call $logical/Obj#constructor + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store $0 + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 1056 + i32.const 92 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1524 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $0 + i64.const 0 + i64.store $0 + local.get $0 + i32.const 5 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store $0 + global.get $~lib/memory/__stack_pointer + local.tee $1 + local.get $0 + i32.store $0 offset=4 + local.get $1 + local.get $0 + call $logical/A#constructor + local.tee $0 + i32.store $0 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + global.set $logical/b + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1524 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $0 + i64.const 0 + i64.store $0 + local.get $0 + i32.const 7 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store $0 + global.get $~lib/memory/__stack_pointer + local.tee $1 + local.get $0 + i32.store $0 offset=4 + local.get $1 + local.get $0 + call $logical/A#constructor + local.tee $0 + i32.store $0 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + global.set $logical/c + global.get $~lib/memory/__stack_pointer + global.get $logical/b + local.tee $0 + i32.store $0 + local.get $0 + local.get $0 + global.get $logical/c + local.get $0 + select + i32.ne + if + i32.const 0 + i32.const 1056 + i32.const 106 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + global.get $logical/b + local.tee $0 + i32.store $0 + global.get $logical/c + local.tee $1 + local.get $0 + local.get $0 + select + local.get $1 + i32.ne + if + i32.const 0 + i32.const 1056 + i32.const 112 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + br $__inlined_func$start:logical + end + i32.const 34320 + i32.const 34368 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $logical/Obj#constructor (type $none_=>_i32) (result i32) + (local $0 i32) + (local $1 i32) global.get $~lib/memory/__stack_pointer - i32.const 4 + i32.const 8 i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1512 + i32.const 1524 i32.lt_s if - i32.const 34304 - i32.const 34352 + i32.const 34320 + i32.const 34368 i32.const 1 i32.const 1 call $~lib/builtins/abort unreachable end global.get $~lib/memory/__stack_pointer - i32.const 0 - i32.store $0 - memory.size $0 - i32.const 16 - i32.shl - i32.const 34280 - i32.sub - i32.const 1 - i32.shr_u - global.set $~lib/rt/itcms/threshold - i32.const 1220 - i32.const 1216 - i32.store $0 - i32.const 1224 - i32.const 1216 - i32.store $0 - i32.const 1216 - global.set $~lib/rt/itcms/pinSpace - i32.const 1252 - i32.const 1248 - i32.store $0 - i32.const 1256 - i32.const 1248 - i32.store $0 - i32.const 1248 - global.set $~lib/rt/itcms/toSpace - i32.const 1396 - i32.const 1392 - i32.store $0 - i32.const 1400 - i32.const 1392 + local.tee $0 + i64.const 0 + i64.store $0 + local.get $0 + i32.const 4 + call $~lib/rt/itcms/__new + local.tee $0 i32.store $0 - i32.const 1392 - global.set $~lib/rt/itcms/fromSpace - call $logical/Obj#constructor - local.set $0 global.get $~lib/memory/__stack_pointer + local.tee $1 + local.get $0 + i32.store $0 offset=4 + local.get $1 local.get $0 + call $~lib/object/Object#constructor + local.tee $0 i32.store $0 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer local.get $0 - i32.eqz + ) + (func $logical/A#constructor (type $i32_=>_i32) (param $0 i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1524 + i32.lt_s if - i32.const 0 - i32.const 1056 - i32.const 87 + i32.const 34320 + i32.const 34368 + i32.const 1 i32.const 1 call $~lib/builtins/abort unreachable end - call $logical/Obj#constructor - local.set $0 global.get $~lib/memory/__stack_pointer - local.get $0 - i32.store $0 + i64.const 0 + i64.store $0 local.get $0 i32.eqz if - i32.const 0 - i32.const 1056 - i32.const 92 - i32.const 1 - call $~lib/builtins/abort - unreachable - end - i32.const 34280 - global.set $~lib/memory/__stack_pointer - global.get $~lib/rt/itcms/state - i32.const 0 - i32.gt_s - if - loop $while-continue|0 - global.get $~lib/rt/itcms/state - if - call $~lib/rt/itcms/step - drop - br $while-continue|0 - end - end - end - call $~lib/rt/itcms/step - drop - loop $while-continue|1 - global.get $~lib/rt/itcms/state - if - call $~lib/rt/itcms/step - drop - br $while-continue|1 - end + global.get $~lib/memory/__stack_pointer + i32.const 6 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store $0 end - global.get $~lib/rt/itcms/total - i64.extend_i32_u - i64.const 200 - i64.mul - i64.const 100 - i64.div_u - i32.wrap_i64 - i32.const 1024 - i32.add - global.set $~lib/rt/itcms/threshold global.get $~lib/memory/__stack_pointer - i32.const 4 + local.tee $1 + local.get $0 + i32.store $0 offset=4 + local.get $1 + local.get $0 + call $~lib/object/Object#constructor + local.tee $0 + i32.store $0 + global.get $~lib/memory/__stack_pointer + i32.const 8 i32.add global.set $~lib/memory/__stack_pointer + local.get $0 ) - (func $logical/Obj#constructor (type $none_=>_i32) (result i32) - (local $0 i32) - (local $1 i32) + (func $~lib/object/Object#constructor (type $i32_=>_i32) (param $0 i32) (result i32) global.get $~lib/memory/__stack_pointer - i32.const 8 + i32.const 4 i32.sub global.set $~lib/memory/__stack_pointer - block $folding-inner0 - global.get $~lib/memory/__stack_pointer - i32.const 1512 - i32.lt_s - br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1524 + i32.lt_s + if + i32.const 34320 + i32.const 34368 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store $0 + local.get $0 + i32.eqz + if global.get $~lib/memory/__stack_pointer - local.tee $0 - i64.const 0 - i64.store $0 - local.get $0 - i32.const 4 + i32.const 0 call $~lib/rt/itcms/__new local.tee $0 i32.store $0 - global.get $~lib/memory/__stack_pointer - local.tee $1 - local.get $0 - i32.store $0 offset=4 - local.get $1 - i32.const 4 - i32.sub - global.set $~lib/memory/__stack_pointer - global.get $~lib/memory/__stack_pointer - i32.const 1512 - i32.lt_s - br_if $folding-inner0 - global.get $~lib/memory/__stack_pointer - i32.const 0 - i32.store $0 - local.get $0 - i32.eqz - if - global.get $~lib/memory/__stack_pointer - i32.const 0 - call $~lib/rt/itcms/__new - local.tee $0 - i32.store $0 - end - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - local.get $1 - local.get $0 - i32.store $0 - global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.add - global.set $~lib/memory/__stack_pointer - local.get $0 - return end - i32.const 34304 - i32.const 34352 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 ) (func $byn-split-outlined-A$~lib/rt/itcms/__visit (type $i32_=>_none) (param $0 i32) global.get $~lib/rt/itcms/white diff --git a/tests/compiler/logical.ts b/tests/compiler/logical.ts index cad6dbed6e..62a26f9d66 100644 --- a/tests/compiler/logical.ts +++ b/tests/compiler/logical.ts @@ -91,5 +91,23 @@ function testContextualBoolOr(someObj: Obj, someInt: i32): bool { } assert(testContextualBoolOr(new Obj(), 0)); -__stack_pointer = __heap_base; -__collect(); +// Common type + +class A {} +class B extends A {} +class C extends A {} + +let b = new B(); +let c = new C(); + +function commonOr(b: B | null): A { + return b || c; +} +assert(commonOr(b) == b); +assert(commonOr(null) == c); + +function commonAnd(b: B | null): A | null { + return b && c; +} +assert(commonAnd(b) == c); +assert(commonAnd(null) == null);