Skip to content
Merged
83 changes: 56 additions & 27 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
}
Expand Down Expand Up @@ -8945,7 +8974,7 @@ export class Compiler extends DiagnosticEmitter {

private compileTernaryExpression(
expression: TernaryExpression,
ctxType: Type,
contextualType: Type,
constraints: Constraints
): ExpressionRef {
let module = this.module;
Expand All @@ -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;
Expand All @@ -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);
Expand Down
40 changes: 40 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
21 changes: 21 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Class>();
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);
Expand Down
Loading