Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4b3b390

Browse files
authored
Add LUB computation for class types (#2594)
BREAKING CHANGE: Binary and ternary expressions now compute and evaluate to the least upper bound of two not identical class type inputs in the absence of a better fitting contextual type. Technically a breaking change, yet likely without noticeable effects on existing code.
1 parent a1434b2 commit 4b3b390

File tree

9 files changed

+782
-288
lines changed

9 files changed

+782
-288
lines changed

‎src/compiler.ts‎

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3779,7 +3779,7 @@ export class Compiler extends DiagnosticEmitter {
37793779

37803780
rightExpr = this.compileExpression(right, leftType);
37813781
rightType = this.currentType;
3782-
commonType = Type.commonDenominator(leftType, rightType, true);
3782+
commonType = Type.commonType(leftType, rightType,contextualType, true);
37833783
if (!commonType || !commonType.isNumericValue) {
37843784
this.error(
37853785
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -3814,7 +3814,7 @@ export class Compiler extends DiagnosticEmitter {
38143814

38153815
rightExpr = this.compileExpression(right, leftType);
38163816
rightType = this.currentType;
3817-
commonType = Type.commonDenominator(leftType, rightType, true);
3817+
commonType = Type.commonType(leftType, rightType,contextualType, true);
38183818
if (!commonType || !commonType.isNumericValue) {
38193819
this.error(
38203820
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -3849,7 +3849,7 @@ export class Compiler extends DiagnosticEmitter {
38493849

38503850
rightExpr = this.compileExpression(right, leftType);
38513851
rightType = this.currentType;
3852-
commonType = Type.commonDenominator(leftType, rightType, true);
3852+
commonType = Type.commonType(leftType, rightType,contextualType, true);
38533853
if (!commonType || !commonType.isNumericValue) {
38543854
this.error(
38553855
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -3884,7 +3884,7 @@ export class Compiler extends DiagnosticEmitter {
38843884

38853885
rightExpr = this.compileExpression(right, leftType);
38863886
rightType = this.currentType;
3887-
commonType = Type.commonDenominator(leftType, rightType, true);
3887+
commonType = Type.commonType(leftType, rightType,contextualType, true);
38883888
if (!commonType || !commonType.isNumericValue) {
38893889
this.error(
38903890
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -3921,7 +3921,7 @@ export class Compiler extends DiagnosticEmitter {
39213921

39223922
rightExpr = this.compileExpression(right, leftType);
39233923
rightType = this.currentType;
3924-
commonType = Type.commonDenominator(leftType, rightType, false);
3924+
commonType = Type.commonType(leftType, rightType, contextualType);
39253925
if (!commonType) {
39263926
this.error(
39273927
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -3973,7 +3973,7 @@ export class Compiler extends DiagnosticEmitter {
39733973

39743974
rightExpr = this.compileExpression(right, leftType);
39753975
rightType = this.currentType;
3976-
commonType = Type.commonDenominator(leftType, rightType, false);
3976+
commonType = Type.commonType(leftType, rightType, contextualType);
39773977
if (!commonType) {
39783978
this.error(
39793979
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4038,7 +4038,7 @@ export class Compiler extends DiagnosticEmitter {
40384038
} else {
40394039
rightExpr = this.compileExpression(right, leftType);
40404040
rightType = this.currentType;
4041-
commonType = Type.commonDenominator(leftType, rightType, false);
4041+
commonType = Type.commonType(leftType, rightType, contextualType);
40424042
if (!commonType || !commonType.isNumericValue) {
40434043
this.error(
40444044
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4083,7 +4083,7 @@ export class Compiler extends DiagnosticEmitter {
40834083
} else {
40844084
rightExpr = this.compileExpression(right, leftType);
40854085
rightType = this.currentType;
4086-
commonType = Type.commonDenominator(leftType, rightType, false);
4086+
commonType = Type.commonType(leftType, rightType, contextualType);
40874087
if (!commonType || !leftType.isNumericValue) {
40884088
this.error(
40894089
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4128,7 +4128,7 @@ export class Compiler extends DiagnosticEmitter {
41284128
} else {
41294129
rightExpr = this.compileExpression(right, leftType);
41304130
rightType = this.currentType;
4131-
commonType = Type.commonDenominator(leftType, rightType, false);
4131+
commonType = Type.commonType(leftType, rightType, contextualType);
41324132
if (!commonType || !commonType.isNumericValue) {
41334133
this.error(
41344134
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4173,7 +4173,7 @@ export class Compiler extends DiagnosticEmitter {
41734173
} else {
41744174
rightExpr = this.compileExpression(right, leftType);
41754175
rightType = this.currentType;
4176-
commonType = Type.commonDenominator(leftType, rightType, false);
4176+
commonType = Type.commonType(leftType, rightType, contextualType);
41774177
if (!commonType || !commonType.isNumericValue) {
41784178
this.error(
41794179
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4218,7 +4218,7 @@ export class Compiler extends DiagnosticEmitter {
42184218
} else {
42194219
rightExpr = this.compileExpression(right, leftType);
42204220
rightType = this.currentType;
4221-
commonType = Type.commonDenominator(leftType, rightType, false);
4221+
commonType = Type.commonType(leftType, rightType, contextualType);
42224222
if (!commonType || !commonType.isNumericValue) {
42234223
this.error(
42244224
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4263,7 +4263,7 @@ export class Compiler extends DiagnosticEmitter {
42634263
} else {
42644264
rightExpr = this.compileExpression(right, leftType);
42654265
rightType = this.currentType;
4266-
commonType = Type.commonDenominator(leftType, rightType, false);
4266+
commonType = Type.commonType(leftType, rightType, contextualType);
42674267
if (!commonType || !commonType.isNumericValue) {
42684268
this.error(
42694269
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4390,7 +4390,7 @@ export class Compiler extends DiagnosticEmitter {
43904390
} else {
43914391
rightExpr = this.compileExpression(right, leftType);
43924392
rightType = this.currentType;
4393-
commonType = Type.commonDenominator(leftType, rightType, false);
4393+
commonType = Type.commonType(leftType, rightType, contextualType);
43944394
if (!commonType || !commonType.isIntegerValue) {
43954395
this.error(
43964396
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4435,7 +4435,7 @@ export class Compiler extends DiagnosticEmitter {
44354435
} else {
44364436
rightExpr = this.compileExpression(right, leftType);
44374437
rightType = this.currentType;
4438-
commonType = Type.commonDenominator(leftType, rightType, false);
4438+
commonType = Type.commonType(leftType, rightType, contextualType);
44394439
if (!commonType || !commonType.isIntegerValue) {
44404440
this.error(
44414441
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4480,7 +4480,7 @@ export class Compiler extends DiagnosticEmitter {
44804480
} else {
44814481
rightExpr = this.compileExpression(right, leftType);
44824482
rightType = this.currentType;
4483-
commonType = Type.commonDenominator(leftType, rightType, false);
4483+
commonType = Type.commonType(leftType, rightType, contextualType);
44844484
if (!commonType || !commonType.isIntegerValue) {
44854485
this.error(
44864486
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
@@ -4537,8 +4537,21 @@ export class Compiler extends DiagnosticEmitter {
45374537
this.currentType = Type.bool;
45384538

45394539
} else {
4540-
rightExpr = this.compileExpression(right, leftType, inheritedConstraints|Constraints.ConvImplicit);
4540+
rightExpr = this.compileExpression(right, leftType, inheritedConstraints);
45414541
rightType = this.currentType;
4542+
commonType = Type.commonType(leftType, rightType, contextualType);
4543+
if (!commonType) {
4544+
this.error(
4545+
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
4546+
expression.range, "&&", leftType.toString(), rightType.toString()
4547+
);
4548+
this.currentType = contextualType;
4549+
return module.unreachable();
4550+
}
4551+
leftExpr = this.convertExpression(leftExpr, leftType, commonType, false, left);
4552+
leftType = commonType;
4553+
rightExpr = this.convertExpression(rightExpr, rightType, commonType, false, right);
4554+
rightType = commonType;
45424555

45434556
// simplify if copying left is trivial
45444557
if (expr = module.tryCopyTrivialExpression(leftExpr)) {
@@ -4562,7 +4575,7 @@ export class Compiler extends DiagnosticEmitter {
45624575
flow.mergeBranch(rightFlow); // LHS && RHS -> RHS conditionally executes
45634576
flow.noteThen(expr, rightFlow); // LHS && RHS == true -> RHS always executes
45644577
this.currentFlow = flow;
4565-
this.currentType = leftType;
4578+
this.currentType = commonType;
45664579
}
45674580
break;
45684581
}
@@ -4603,8 +4616,22 @@ export class Compiler extends DiagnosticEmitter {
46034616
this.currentType = Type.bool;
46044617

46054618
} else {
4606-
rightExpr = this.compileExpression(right, leftType, inheritedConstraints|Constraints.ConvImplicit);
4619+
rightExpr = this.compileExpression(right, leftType, inheritedConstraints);
46074620
rightType = this.currentType;
4621+
commonType = Type.commonType(leftType, rightType, contextualType);
4622+
if (!commonType) {
4623+
this.error(
4624+
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
4625+
expression.range, "||", leftType.toString(), rightType.toString()
4626+
);
4627+
this.currentType = contextualType;
4628+
return module.unreachable();
4629+
}
4630+
let possiblyNull = leftType.is(TypeFlags.Nullable) && rightType.is(TypeFlags.Nullable);
4631+
leftExpr = this.convertExpression(leftExpr, leftType, commonType, false, left);
4632+
leftType = commonType;
4633+
rightExpr = this.convertExpression(rightExpr, rightType, commonType, false, right);
4634+
rightType = commonType;
46084635

46094636
// simplify if copying left is trivial
46104637
if (expr = module.tryCopyTrivialExpression(leftExpr)) {
@@ -4629,7 +4656,9 @@ export class Compiler extends DiagnosticEmitter {
46294656
flow.mergeBranch(rightFlow); // LHS || RHS -> RHS conditionally executes
46304657
flow.noteElse(expr, rightFlow); // LHS || RHS == false -> RHS always executes
46314658
this.currentFlow = flow;
4632-
this.currentType = leftType;
4659+
this.currentType = possiblyNull
4660+
? commonType
4661+
: commonType.nonNullableType;
46334662
}
46344663
break;
46354664
}
@@ -8945,7 +8974,7 @@ export class Compiler extends DiagnosticEmitter {
89458974

89468975
private compileTernaryExpression(
89478976
expression: TernaryExpression,
8948-
ctxType: Type,
8977+
contextualType: Type,
89498978
constraints: Constraints
89508979
): ExpressionRef {
89518980
let module = this.module;
@@ -8958,24 +8987,24 @@ export class Compiler extends DiagnosticEmitter {
89588987
// FIXME: skips common denominator, inconsistently picking branch type
89598988
let condKind = this.evaluateCondition(condExprTrueish);
89608989
if (condKind == ConditionKind.True) {
8961-
return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifThen, ctxType));
8990+
return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifThen, contextualType));
89628991
}
89638992
if (condKind == ConditionKind.False) {
8964-
return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifElse, ctxType));
8993+
return module.maybeDropCondition(condExprTrueish, this.compileExpression(ifElse, contextualType));
89658994
}
89668995

89678996
let outerFlow = this.currentFlow;
89688997
let ifThenFlow = outerFlow.forkThen(condExpr);
89698998
this.currentFlow = ifThenFlow;
8970-
let ifThenExpr = this.compileExpression(ifThen, ctxType);
8999+
let ifThenExpr = this.compileExpression(ifThen, contextualType);
89719000
let ifThenType = this.currentType;
89729001

89739002
let ifElseFlow = outerFlow.forkElse(condExpr);
89749003
this.currentFlow = ifElseFlow;
8975-
let ifElseExpr = this.compileExpression(ifElse, ctxType == Type.auto ? ifThenType : ctxType);
9004+
let ifElseExpr = this.compileExpression(ifElse, contextualType == Type.auto ? ifThenType : contextualType);
89769005
let ifElseType = this.currentType;
89779006

8978-
if (ctxType == Type.void) { // values, including type mismatch, are irrelevant
9007+
if (contextualType == Type.void) { // values, including type mismatch, are irrelevant
89799008
if (ifThenType != Type.void) {
89809009
ifThenExpr = module.drop(ifThenExpr);
89819010
ifThenType = Type.void;
@@ -8986,13 +9015,13 @@ export class Compiler extends DiagnosticEmitter {
89869015
}
89879016
this.currentType = Type.void;
89889017
} else {
8989-
let commonType = Type.commonDenominator(ifThenType, ifElseType, false);
9018+
let commonType = Type.commonType(ifThenType, ifElseType, contextualType);
89909019
if (!commonType) {
89919020
this.error(
89929021
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
89939022
ifElse.range, ifElseType.toString(), ifThenType.toString()
89949023
);
8995-
this.currentType = ctxType;
9024+
this.currentType = contextualType;
89969025
return module.unreachable();
89979026
}
89989027
ifThenExpr = this.convertExpression(ifThenExpr, ifThenType, commonType, false, ifThen);

‎src/module.ts‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,46 @@ export namespace HeapTypeRef {
128128
export function isSubtype(ht: HeapTypeRef, superHt: HeapTypeRef): bool {
129129
return binaryen._BinaryenHeapTypeIsSubType(ht, superHt);
130130
}
131+
132+
export function leastUpperBound(a: HeapTypeRef, b: HeapTypeRef): HeapTypeRef {
133+
// see binaryen/src/wasm/wasm-type.cpp
134+
if (a == b) return a;
135+
if (getBottom(a) != getBottom(b)) return -1;
136+
if (isBottom(a)) return b;
137+
if (isBottom(b)) return a;
138+
if (a > b) {
139+
let t = a;
140+
a = b;
141+
b = t;
142+
}
143+
switch (a) {
144+
case HeapTypeRef.Extern:
145+
case HeapTypeRef.Func: return -1;
146+
case HeapTypeRef.Any: return a;
147+
case HeapTypeRef.Eq: {
148+
return b == HeapTypeRef.I31 || b == HeapTypeRef.Data || b == HeapTypeRef.Array
149+
? HeapTypeRef.Eq
150+
: HeapTypeRef.Any;
151+
}
152+
case HeapTypeRef.I31: {
153+
return b == HeapTypeRef.Data || b == HeapTypeRef.Array
154+
? HeapTypeRef.Eq
155+
: HeapTypeRef.Any;
156+
}
157+
case HeapTypeRef.Data: {
158+
return b == HeapTypeRef.Array
159+
? HeapTypeRef.Data
160+
: HeapTypeRef.Any;
161+
}
162+
case HeapTypeRef.Array:
163+
case HeapTypeRef.String:
164+
case HeapTypeRef.StringviewWTF8:
165+
case HeapTypeRef.StringviewWTF16:
166+
case HeapTypeRef.StringviewIter: return HeapTypeRef.Any;
167+
}
168+
assert(false);
169+
return -1;
170+
}
131171
}
132172

133173
/** Packed array element respectively struct field types. */

‎src/program.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4415,6 +4415,27 @@ export class Class extends TypedElement {
44154415
registerConcreteElement(program, this);
44164416
}
44174417

4418+
/** Computes the least upper bound of two class types. */
4419+
static leastUpperBound(a: Class, b: Class): Class | null {
4420+
if (a == b) return a;
4421+
let candidates = new Set<Class>();
4422+
candidates.add(a);
4423+
candidates.add(b);
4424+
while (true) {
4425+
let aBase = a.base;
4426+
let bBase = b.base;
4427+
if (!aBase && !bBase) return null; // none
4428+
if (aBase) {
4429+
if (candidates.has(aBase)) return aBase;
4430+
candidates.add(a = aBase);
4431+
}
4432+
if (bBase) {
4433+
if (candidates.has(bBase)) return bBase;
4434+
candidates.add(b = bBase);
4435+
}
4436+
}
4437+
}
4438+
44184439
/** Sets the base class. */
44194440
setBase(base: Class): void {
44204441
assert(!this.base);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /