Skip to content

Commit dfc8a65

Browse files
authored
fix: strictly check operator overload ambiguity (#2762)
Operator overload is an internal mechanism, it can support to compare user-defined class, but it need to be limited. a == b and b == a should run the same function and get the same result in semantic level. Allowing ambiguity is bug prone.
1 parent 1847c8f commit dfc8a65

10 files changed

+4549
-233
lines changed

src/compiler.ts

+185-230
Large diffs are not rendered by default.

src/diagnosticMessages.json

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"Index signature accessors in type '{0}' differ in types.": 237,
5252
"Initializer, definitive assignment or nullable type expected.": 238,
5353
"Definitive assignment has no effect on local variables.": 239,
54+
"Ambiguous operator overload '{0}' (conflicting overloads '{1}' and '{2}').": 240,
5455

5556
"Importing the table disables some indirect call optimizations.": 901,
5657
"Exporting the table disables some indirect call optimizations.": 902,

src/program.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,10 @@ export namespace OperatorKind {
390390
case Token.GreaterThan_GreaterThan_Equals: return OperatorKind.BitwiseShr;
391391
case Token.GreaterThan_GreaterThan_GreaterThan:
392392
case Token.GreaterThan_GreaterThan_GreaterThan_Equals: return OperatorKind.BitwiseShrU;
393-
case Token.Equals_Equals: return OperatorKind.Eq;
394-
case Token.Exclamation_Equals: return OperatorKind.Ne;
393+
case Token.Equals_Equals:
394+
case Token.Equals_Equals_Equals: return OperatorKind.Eq;
395+
case Token.Exclamation_Equals:
396+
case Token.Exclamation_Equals_Equals: return OperatorKind.Ne;
395397
case Token.GreaterThan: return OperatorKind.Gt;
396398
case Token.GreaterThan_Equals: return OperatorKind.Ge;
397399
case Token.LessThan: return OperatorKind.Lt;

src/types.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import {
1010
import {
1111
Class,
1212
Program,
13-
DecoratorFlags
13+
DecoratorFlags,
14+
OperatorKind,
15+
Function
1416
} from "./program";
1517

1618
import {
@@ -320,6 +322,14 @@ export class Type {
320322
return null;
321323
}
322324

325+
lookupOverload(kind: OperatorKind, program: Program): Function | null {
326+
let classReference = this.getClassOrWrapper(program);
327+
if (classReference) {
328+
return classReference.lookupOverload(kind);
329+
}
330+
return null;
331+
}
332+
323333
/** Gets the underlying function signature of this type, if any. */
324334
getSignature(): Signature | null {
325335
return this.isInternalReference
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"asc_flags": [],
3+
"stderr": [
4+
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/A#__eq' and 'operator-overload-ambiguity/B#__eq').",
5+
"compare_nonnull_a == compare_nonnull_b;",
6+
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/B#__eq' and 'operator-overload-ambiguity/A#__eq').",
7+
"compare_nonnull_b == compare_nonnull_a;",
8+
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/C.__eq' and 'operator-overload-ambiguity/D#__eq').",
9+
"compare_nonnull_c == compare_nonnull_d;",
10+
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/A#__eq' and 'operator-overload-ambiguity/B#__eq').",
11+
"compare_extend_1 == compare_extend_2;",
12+
"EOF"
13+
]
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
class A {
2+
@operator("==") __eq(other: B): bool {
3+
return true;
4+
}
5+
}
6+
class B {
7+
@operator("==") __eq(other: A): bool {
8+
return true;
9+
}
10+
}
11+
export function compare_nonnull(compare_nonnull_a: A, compare_nonnull_b: B): void {
12+
compare_nonnull_a == compare_nonnull_b;
13+
compare_nonnull_b == compare_nonnull_a;
14+
}
15+
16+
class C {
17+
@operator("==") static __eq(self: C | null, other: D | null): bool {
18+
return true;
19+
}
20+
}
21+
class D {
22+
@operator("==") __eq(other: i32): bool {
23+
return true;
24+
}
25+
}
26+
export function compare_null(compare_nonnull_c: C | null, compare_nonnull_d: D | null): void {
27+
compare_nonnull_c == compare_nonnull_d;
28+
}
29+
30+
class PA extends A {}
31+
class PB extends B {}
32+
export function compare_extend(compare_extend_1: PA, compare_extend_2: PB): void {
33+
compare_extend_1 == compare_extend_2;
34+
}
35+
36+
export function end(): void {
37+
ERROR("EOF");
38+
}

0 commit comments

Comments
 (0)