Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,10 @@ mlir::Value LogicOp(Operation *binOp, SyntaxKind op, mlir::Value left, mlir::Typ
return cmpOp;
}

return LogicOp<StdIOpTy, V1, v1, StdFOpTy, V2, v2>(binOp, op, left, baseType, right, rightType, builder, typeConverter, compileOptions);
// the union is the right operand, so it is the right type that becomes its base type:
// passing the base type as the left type and the union again as the right one came
// straight back here, until the compiler's stack ran out (`opaque != (opaque | null)`)
return LogicOp<StdIOpTy, V1, v1, StdFOpTy, V2, v2>(binOp, op, left, leftType, right, baseType, builder, typeConverter, compileOptions);
}

// excluded string
Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ tslang_add_test(NAME test-compile-00-optional-number-compare COMMAND test-runner
tslang_add_test(NAME test-compile-00-optional-loose-null COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00optional_loose_null.ts")
tslang_add_test(NAME test-compile-00-union-nullable-array COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_nullable_array.ts")
tslang_add_test(NAME test-compile-00-union-nullable-value COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_nullable_value.ts")
tslang_add_test(NAME test-compile-00-opaque-nullable-compare COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00opaque_nullable_compare.ts")
tslang_add_test(NAME test-compile-00-union-value-equality COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_value_equality.ts")
tslang_add_test(NAME test-compile-00-strict-equals-mismatched-types COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00strict_equals_mismatched_types.ts")
tslang_add_test(NAME test-compile-00-any-number-compare COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00any_number_compare.ts")
Expand Down Expand Up @@ -937,6 +938,7 @@ tslang_add_test(NAME test-jit-00-optional-number-compare COMMAND test-runner -ji
tslang_add_test(NAME test-jit-00-optional-loose-null COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00optional_loose_null.ts")
tslang_add_test(NAME test-jit-00-union-nullable-array COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_nullable_array.ts")
tslang_add_test(NAME test-jit-00-union-nullable-value COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_nullable_value.ts")
tslang_add_test(NAME test-jit-00-opaque-nullable-compare COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00opaque_nullable_compare.ts")
tslang_add_test(NAME test-jit-00-union-value-equality COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_value_equality.ts")
tslang_add_test(NAME test-jit-00-strict-equals-mismatched-types COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00strict_equals_mismatched_types.ts")
tslang_add_test(NAME test-jit-00-any-number-compare COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00any_number_compare.ts")
Expand Down Expand Up @@ -2178,6 +2180,7 @@ set(TSLANG_CORPUS
typeGuardOfFormTypeOfBoolean.ts
types_vs_interfaces.ts
00any_number_compare.ts
00opaque_nullable_compare.ts
)

# the default model runs these ahead of time only, so the other models do too
Expand Down
61 changes: 61 additions & 0 deletions tslang/test/tester/tests/00opaque_nullable_compare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Comparing an Opaque with an `Opaque | null` overflowed the compiler's stack (0xC00000FD, no
// message): the comparison's lowering kept calling itself with the same operand types.

class State {
current: Opaque | null;

constructor() {
this.current = null;
}

set(next: Opaque): boolean {
if (next != this.current) {
this.current = next;
return true;
}

return false;
}
}

class Item {
id = 1;
}

function sameOpaque(a: Opaque, b: Opaque | null) {
return a == b;
}

function sameOpaqueReversed(a: Opaque | null, b: Opaque) {
return a == b;
}

function sameItem(a: Item, b: Item | null) {
return a === b;
}

function differentItem(a: Item, b: Item | null) {
return a !== b;
}

function main() {
const s = new State();
const h = <Opaque>new Item();
const g = <Opaque>new Item();

assert(s.set(h), "first set binds");
assert(!s.set(h), "same handle again is skipped");
assert(s.set(g), "another handle binds");

assert(sameOpaque(h, h), "h == h");
assert(!sameOpaque(h, g), "h == g");
assert(!sameOpaque(h, null), "h == null");
assert(sameOpaqueReversed(h, h), "h == h (union on the left)");

const item = new Item();
assert(sameItem(item, item), "item === item");
assert(!sameItem(item, null), "item === null");
assert(differentItem(item, new Item()), "item !== other");

print("done.");
}
Loading