Skip to content

Commit

Permalink
fix: strictly check operator overload ambiguity (#2762)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
HerrCai0907 authored Sep 26, 2024
1 parent 1847c8f commit dfc8a65
Show file tree
Hide file tree
Showing 10 changed files with 4,549 additions and 233 deletions.
415 changes: 185 additions & 230 deletions src/compiler.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"Index signature accessors in type '{0}' differ in types.": 237,
"Initializer, definitive assignment or nullable type expected.": 238,
"Definitive assignment has no effect on local variables.": 239,
"Ambiguous operator overload '{0}' (conflicting overloads '{1}' and '{2}').": 240,

"Importing the table disables some indirect call optimizations.": 901,
"Exporting the table disables some indirect call optimizations.": 902,
Expand Down
6 changes: 4 additions & 2 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,10 @@ export namespace OperatorKind {
case Token.GreaterThan_GreaterThan_Equals: return OperatorKind.BitwiseShr;
case Token.GreaterThan_GreaterThan_GreaterThan:
case Token.GreaterThan_GreaterThan_GreaterThan_Equals: return OperatorKind.BitwiseShrU;
case Token.Equals_Equals: return OperatorKind.Eq;
case Token.Exclamation_Equals: return OperatorKind.Ne;
case Token.Equals_Equals:
case Token.Equals_Equals_Equals: return OperatorKind.Eq;
case Token.Exclamation_Equals:
case Token.Exclamation_Equals_Equals: return OperatorKind.Ne;
case Token.GreaterThan: return OperatorKind.Gt;
case Token.GreaterThan_Equals: return OperatorKind.Ge;
case Token.LessThan: return OperatorKind.Lt;
Expand Down
12 changes: 11 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
import {
Class,
Program,
DecoratorFlags
DecoratorFlags,
OperatorKind,
Function
} from "./program";

import {
Expand Down Expand Up @@ -320,6 +322,14 @@ export class Type {
return null;
}

lookupOverload(kind: OperatorKind, program: Program): Function | null {
let classReference = this.getClassOrWrapper(program);
if (classReference) {
return classReference.lookupOverload(kind);
}
return null;
}

/** Gets the underlying function signature of this type, if any. */
getSignature(): Signature | null {
return this.isInternalReference
Expand Down
14 changes: 14 additions & 0 deletions tests/compiler/operator-overload-ambiguity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"asc_flags": [],
"stderr": [
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/A#__eq' and 'operator-overload-ambiguity/B#__eq').",
"compare_nonnull_a == compare_nonnull_b;",
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/B#__eq' and 'operator-overload-ambiguity/A#__eq').",
"compare_nonnull_b == compare_nonnull_a;",
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/C.__eq' and 'operator-overload-ambiguity/D#__eq').",
"compare_nonnull_c == compare_nonnull_d;",
"AS240: Ambiguous operator overload '==' (conflicting overloads 'operator-overload-ambiguity/A#__eq' and 'operator-overload-ambiguity/B#__eq').",
"compare_extend_1 == compare_extend_2;",
"EOF"
]
}
38 changes: 38 additions & 0 deletions tests/compiler/operator-overload-ambiguity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class A {
@operator("==") __eq(other: B): bool {
return true;
}
}
class B {
@operator("==") __eq(other: A): bool {
return true;
}
}
export function compare_nonnull(compare_nonnull_a: A, compare_nonnull_b: B): void {
compare_nonnull_a == compare_nonnull_b;
compare_nonnull_b == compare_nonnull_a;
}

class C {
@operator("==") static __eq(self: C | null, other: D | null): bool {
return true;
}
}
class D {
@operator("==") __eq(other: i32): bool {
return true;
}
}
export function compare_null(compare_nonnull_c: C | null, compare_nonnull_d: D | null): void {
compare_nonnull_c == compare_nonnull_d;
}

class PA extends A {}
class PB extends B {}
export function compare_extend(compare_extend_1: PA, compare_extend_2: PB): void {
compare_extend_1 == compare_extend_2;
}

export function end(): void {
ERROR("EOF");
}
Loading

0 comments on commit dfc8a65

Please sign in to comment.