Skip to content

Add LUB computation for class types #2594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jan 6, 2023
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