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
62 changes: 62 additions & 0 deletions tslang/include/TypeScript/MLIRLogic/MLIRTypeHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,50 @@ class MLIRTypeHelper
return mlir::Type();
}

// index or a signless integer (a constant string's folded length is i32) with a signed integer (or a
// signed integer literal) merges to a signed integer that holds both
mlir::Type mergeIndexWithSignedInt(mlir::Type typeLeft, mlir::Type typeRight, bool& found)
{
found = false;

auto intOf = [](mlir::Type type) -> mlir::IntegerType {
if (auto literalType = dyn_cast<mlir_ts::LiteralType>(type))
{
type = literalType.getElementType();
}

return dyn_cast<mlir::IntegerType>(type);
};

auto isIndexOrSignless = [&](mlir::Type type) {
auto intType = intOf(type);
return type.isIndex() || (intType && intType.isSignless());
};

auto signedIntOf = [&](mlir::Type type) {
auto intType = intOf(type);
return intType && intType.isSigned() ? intType : mlir::IntegerType();
};

mlir::IntegerType signedType;
if (isIndexOrSignless(typeLeft))
{
signedType = signedIntOf(typeRight);
}
else if (isIndexOrSignless(typeRight))
{
signedType = signedIntOf(typeLeft);
}

if (!signedType)
{
return mlir::Type();
}

found = true;
return mlir::IntegerType::get(context, std::max(64u, signedType.getIntOrFloatBitWidth()), mlir::IntegerType::Signed);
}

mlir::Type mergeFuncTypes(mlir::Type typeLeft, mlir::Type typeRight, bool& found)
{
found = false;
Expand Down Expand Up @@ -1785,6 +1829,14 @@ class MLIRTypeHelper
return typeRight;
}

// `c ? s.length : -1` - see mergeType
auto mergedIndex = false;
auto resIndexType = mergeIndexWithSignedInt(typeLeft, typeRight, mergedIndex);
if (mergedIndex)
{
return resIndexType;
}

if (canWideTypeWithoutDataLoss(typeLeft, typeRight))
{
return typeRight;
Expand Down Expand Up @@ -3575,6 +3627,16 @@ class MLIRTypeHelper
return existType;
}

// index (a length, a count) passes for signless in canCastFromTo but holds an unsigned value,
// so picking it for `index | si32` wraps a negative value: `return s.length` + `return -1` gave 4294967295
auto mergedIndex = false;
auto resIndexType = mergeIndexWithSignedInt(existType, currentType, mergedIndex);
if (mergedIndex)
{
merged = true;
return resIndexType;
}

if (canCastFromTo(location, currentType, existType))
{
merged = true;
Expand Down
7 changes: 4 additions & 3 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6039,7 +6039,7 @@ class MLIRGenImpl
{
auto isBoolean = [](mlir::Type type) { return isa<mlir_ts::BooleanType>(type); };
auto isString = [](mlir::Type type) { return isa<mlir_ts::StringType>(type); };
auto isNumeric = [](mlir::Type type) { return type.isIntOrIndexOrFloat() && !isa<mlir_ts::BooleanType>(type); };
auto isNumeric = [](mlir::Type type) { return isa<mlir_ts::NumberType>(type) || (type.isIntOrIndexOrFloat() && !isa<mlir_ts::BooleanType>(type)); };

auto leftIsBoolean = isBoolean(leftType);
auto rightIsBoolean = isBoolean(rightType);
Expand Down Expand Up @@ -6072,8 +6072,9 @@ class MLIRGenImpl
if ((opCode == SyntaxKind::EqualsEqualsEqualsToken || opCode == SyntaxKind::ExclamationEqualsEqualsToken)
&& isDefinitelyMismatchedForStrictEquals(leftExpressionValue.getType(), rightExpressionValue.getType()))
{
auto result = opCode == SyntaxKind::ExclamationEqualsEqualsToken;
leftExpressionValue = builder.create<mlir_ts::ConstantOp>(location, getBooleanType(), builder.getBoolAttr(result));
// the operator itself still runs on the replaced operands, so they must be two
// unequal values: `false === true` is false and `false !== true` is true
leftExpressionValue = builder.create<mlir_ts::ConstantOp>(location, getBooleanType(), builder.getBoolAttr(false));
rightExpressionValue = builder.create<mlir_ts::ConstantOp>(location, getBooleanType(), builder.getBoolAttr(true));
return mlir::success();
}
Expand Down
4 changes: 4 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ tslang_add_test(NAME test-compile-00-optional-loose-null COMMAND test-runner "${
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-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-length-with-negative-merge COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00length_with_negative_merge.ts")
tslang_add_test(NAME test-compile-00-intersection-type COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00intersection_type.ts")
tslang_add_test(NAME test-compile-00-intersection-type-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00intersection_type_generic.ts")
tslang_add_test(NAME test-compile-00-length COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00length.ts")
Expand Down Expand Up @@ -863,6 +865,8 @@ tslang_add_test(NAME test-jit-00-optional-loose-null COMMAND test-runner -jit "$
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-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-length-with-negative-merge COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00length_with_negative_merge.ts")
tslang_add_test(NAME test-jit-00-union-global-init COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_global_init.ts")
tslang_add_test(NAME test-jit-00-intersection-type COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00intersection_type.ts")
tslang_add_test(NAME test-jit-00-intersection-type-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00intersection_type_generic.ts")
Expand Down
49 changes: 49 additions & 0 deletions tslang/test/tester/tests/00length_with_negative_merge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// A length (index, unsigned) and a negative integer in one inferred type: a return, a conditional
// and an array literal must all keep -1, not wrap it to 4294967295
function lengthFirst(p: string, found: boolean) {
if (found) return p.length;
return -1;
}

function negativeFirst(p: number[], found: boolean) {
if (!found) return -1;
return p.length;
}

function returns() {
assert(lengthFirst("abc", true) == 3, "length first: length");
assert(lengthFirst("abc", false) == -1, "length first: -1");
assert(lengthFirst("abc", false) < 0, "length first: -1 < 0");
assert(negativeFirst([1, 2], true) == 2, "-1 first: length");
assert(negativeFirst([1, 2], false) == -1, "-1 first: -1");
}

function conditionals(p: string, found: boolean) {
const a = found ? p.length : -1;
const b = found ? -1 : p.length;
assert(a == -1, "found ? length : -1");
assert(b == 3, "found ? -1 : length");
}

function arrays(p: string) {
const a = [p.length, -1];
const b = [-1, p.length];
assert(a[0] == 3 && a[1] == -1, "[length, -1]");
assert(b[0] == -1 && b[1] == 3, "[-1, length]");
}

function constantString(found: boolean) {
// a constant string's length folds to a constant
const p = "abc";
const a = found ? p.length : -1;
assert(a == -1, "constant: found ? length : -1");
assert([p.length, -1][1] == -1, "constant: [length, -1]");
}

function main() {
returns();
conditionals("abc", false);
arrays("abc");
constantString(false);
print("done.");
}
41 changes: 41 additions & 0 deletions tslang/test/tester/tests/00strict_equals_mismatched_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// `===` / `!==` between a boolean, a number and a string never coerce: the values are simply unequal,
// so `===` is false and `!==` is true - for literals, for consts and for values typed `number`
function literals() {
assert(1 !== true, "1 !== true");
assert("a" !== 1, "\"a\" !== 1");
assert(true !== "x", "true !== \"x\"");
assert(!(1 === true), "1 === true");
assert(!("a" === 1), "\"a\" === 1");
assert(!(true === "x"), "true === \"x\"");
}

function consts() {
const n = 1, b = true, s = "a";
assert(n !== b, "n !== b");
assert(s !== n, "s !== n");
assert(b !== s, "b !== s");
assert(!(n === b), "n === b");
assert(!(s === n), "s === n");
assert(!(b === s), "b === s");
}

function typed() {
let x: number = 1;
let b: boolean = true;
let s: string = "1";
assert(x !== b, "number !== boolean");
assert(x !== s, "number !== string");
assert(s !== x, "string !== number");
assert(!(x === b), "number === boolean");
assert(!(x === s), "number === string");

// loose equality still coerces
assert(x == b, "1 == true");
}

function main() {
literals();
consts();
typed();
print("done.");
}
Loading