Skip to content

Fix #61429: Fast arithmetic type check for type-parameters upper-bounded by bigint fixed #61571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
73 changes: 39 additions & 34 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39252,7 +39252,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
return numberType;
}
return getUnaryResultType(operandType);
return getUnaryArithmeticResultType(operandType);
case SyntaxKind.ExclamationToken:
checkTruthinessOfType(operandType, node.operand);
const facts = getTypeFacts(operandType, TypeFacts.Truthy | TypeFacts.Falsy);
Expand All @@ -39270,7 +39270,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access,
);
}
return getUnaryResultType(operandType);
return getUnaryArithmeticResultType(operandType);
}
return errorType;
}
Expand All @@ -39293,11 +39293,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access,
);
}
return getUnaryResultType(operandType);
return getUnaryArithmeticResultType(operandType);
}

function getUnaryResultType(operandType: Type): Type {
if (maybeTypeOfKind(operandType, TypeFlags.BigIntLike)) {
function getUnaryArithmeticResultType(operandType: Type): Type {
if (maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlags.BigIntLike)) {
return isTypeAssignableToKind(operandType, TypeFlags.AnyOrUnknown) || maybeTypeOfKind(operandType, TypeFlags.NumberLike)
? numberOrBigIntType
: bigintType;
Expand Down Expand Up @@ -40009,35 +40009,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// otherwise just check each operand separately and report errors as normal
const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ true);
const rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ true);
let resultType: Type;
// If both are any or unknown, allow operation; assume it will resolve to number
if (
(isTypeAssignableToKind(leftType, TypeFlags.AnyOrUnknown) && isTypeAssignableToKind(rightType, TypeFlags.AnyOrUnknown)) ||
// Or, if neither could be bigint, implicit coercion results in a number result
!(maybeTypeOfKind(leftType, TypeFlags.BigIntLike) || maybeTypeOfKind(rightType, TypeFlags.BigIntLike))
) {
resultType = numberType;
}
// At least one is assignable to bigint, so check that both are
else if (bothAreBigIntLike(leftType, rightType)) {
switch (operator) {
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
reportOperatorError();
break;
case SyntaxKind.AsteriskAsteriskToken:
case SyntaxKind.AsteriskAsteriskEqualsToken:
if (languageVersion < ScriptTarget.ES2016) {
error(errorNode, Diagnostics.Exponentiation_cannot_be_performed_on_bigint_values_unless_the_target_option_is_set_to_es2016_or_later);
}
}
resultType = bigintType;
}
// Exactly one of leftType/rightType is assignable to bigint
else {
reportOperatorError(bothAreBigIntLike);
resultType = errorType;
}

const resultType = getBinaryArithmeticResultType(leftType, rightType);
if (leftOk && rightOk) {
checkAssignmentOperator(resultType);
switch (operator) {
Expand Down Expand Up @@ -40228,6 +40201,38 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return Debug.fail();
}

function getBinaryArithmeticResultType(leftType: Type, rightType: Type): Type {
if (isTypeAssignableToKind(leftType, TypeFlags.AnyOrUnknown) && isTypeAssignableToKind(rightType, TypeFlags.AnyOrUnknown)) {
// If both are any or unknown, allow operation; assume it will resolve to number
// (This is unsound, but it is not practical for untyped programs to
// have `bigint|number` inferred everywhere; #41741)
return numberType;
}
else if (!maybeTypeOfKindConsideringBaseConstraint(leftType, TypeFlags.BigIntLike) && !maybeTypeOfKindConsideringBaseConstraint(rightType, TypeFlags.BigIntLike)) {
// If neither could be bigint, implicit coercion results in a number result
return numberType;
}
// At least one is assignable to bigint, so check that both are
else if (bothAreBigIntLike(leftType, rightType)) {
switch (operator) {
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
reportOperatorError();
break;
case SyntaxKind.AsteriskAsteriskToken:
case SyntaxKind.AsteriskAsteriskEqualsToken:
if (languageVersion < ScriptTarget.ES2016) {
error(errorNode, Diagnostics.Exponentiation_cannot_be_performed_on_bigint_values_unless_the_target_option_is_set_to_es2016_or_later);
}
}
return bigintType;
}

// Exactly one of leftType/rightType is assignable to bigint
reportOperatorError(bothAreBigIntLike);
return errorType;
}

function bothAreBigIntLike(left: Type, right: Type): boolean {
return isTypeAssignableToKind(left, TypeFlags.BigIntLike) && isTypeAssignableToKind(right, TypeFlags.BigIntLike);
}
Expand Down
77 changes: 77 additions & 0 deletions tests/baselines/reference/bigintSubtypingTypeParameter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//// [tests/cases/compiler/bigintSubtypingTypeParameter.ts] ////

//// [bigintSubtypingTypeParameter.ts]
function bigintSubtypeAdder<T extends bigint>(a: T, b: T): bigint {
const sum = a + b;
return sum;
}

function bigintSubtypeDifference<T extends bigint>(a: T, b: T): bigint {
const difference = a - b;
return difference;
}

function bigintSubtypeArithmeticNegation<T extends bigint>(a: T): bigint {
const negation = -a;
return negation;
}

function bigintSubtypeBitInverse<T extends bigint>(a: T): bigint {
const bitInverse = ~a;
return bitInverse;
}

function bigintSubtypeBitand<T extends bigint>(a: T, b: T): bigint {
const bitand = a & b;
return bitand;
}

function bigintSubtypeBitor<T extends bigint>(a: T, b: T): bigint {
const bitor = a | b;
return bitor;
}

function bigintSubtypeLeftshift<T extends bigint>(a: T, b: T): bigint {
const leftshift = a << b;
return leftshift;
}

function bigintSubtypeRightshift<T extends bigint>(a: T, b: T): bigint {
const rightshift = a >> b;
return rightshift;
}


//// [bigintSubtypingTypeParameter.js]
function bigintSubtypeAdder(a, b) {
var sum = a + b;
return sum;
}
function bigintSubtypeDifference(a, b) {
var difference = a - b;
return difference;
}
function bigintSubtypeArithmeticNegation(a) {
var negation = -a;
return negation;
}
function bigintSubtypeBitInverse(a) {
var bitInverse = ~a;
return bitInverse;
}
function bigintSubtypeBitand(a, b) {
var bitand = a & b;
return bitand;
}
function bigintSubtypeBitor(a, b) {
var bitor = a | b;
return bitor;
}
function bigintSubtypeLeftshift(a, b) {
var leftshift = a << b;
return leftshift;
}
function bigintSubtypeRightshift(a, b) {
var rightshift = a >> b;
return rightshift;
}
133 changes: 133 additions & 0 deletions tests/baselines/reference/bigintSubtypingTypeParameter.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//// [tests/cases/compiler/bigintSubtypingTypeParameter.ts] ////

=== bigintSubtypingTypeParameter.ts ===
function bigintSubtypeAdder<T extends bigint>(a: T, b: T): bigint {
>bigintSubtypeAdder : Symbol(bigintSubtypeAdder, Decl(bigintSubtypingTypeParameter.ts, 0, 0))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 0, 28))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 0, 46))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 0, 28))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 0, 51))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 0, 28))

const sum = a + b;
>sum : Symbol(sum, Decl(bigintSubtypingTypeParameter.ts, 1, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 0, 46))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 0, 51))

return sum;
>sum : Symbol(sum, Decl(bigintSubtypingTypeParameter.ts, 1, 6))
}

function bigintSubtypeDifference<T extends bigint>(a: T, b: T): bigint {
>bigintSubtypeDifference : Symbol(bigintSubtypeDifference, Decl(bigintSubtypingTypeParameter.ts, 3, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 5, 33))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 5, 51))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 5, 33))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 5, 56))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 5, 33))

const difference = a - b;
>difference : Symbol(difference, Decl(bigintSubtypingTypeParameter.ts, 6, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 5, 51))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 5, 56))

return difference;
>difference : Symbol(difference, Decl(bigintSubtypingTypeParameter.ts, 6, 6))
}

function bigintSubtypeArithmeticNegation<T extends bigint>(a: T): bigint {
>bigintSubtypeArithmeticNegation : Symbol(bigintSubtypeArithmeticNegation, Decl(bigintSubtypingTypeParameter.ts, 8, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 10, 41))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 10, 59))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 10, 41))

const negation = -a;
>negation : Symbol(negation, Decl(bigintSubtypingTypeParameter.ts, 11, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 10, 59))

return negation;
>negation : Symbol(negation, Decl(bigintSubtypingTypeParameter.ts, 11, 6))
}

function bigintSubtypeBitInverse<T extends bigint>(a: T): bigint {
>bigintSubtypeBitInverse : Symbol(bigintSubtypeBitInverse, Decl(bigintSubtypingTypeParameter.ts, 13, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 15, 33))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 15, 51))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 15, 33))

const bitInverse = ~a;
>bitInverse : Symbol(bitInverse, Decl(bigintSubtypingTypeParameter.ts, 16, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 15, 51))

return bitInverse;
>bitInverse : Symbol(bitInverse, Decl(bigintSubtypingTypeParameter.ts, 16, 6))
}

function bigintSubtypeBitand<T extends bigint>(a: T, b: T): bigint {
>bigintSubtypeBitand : Symbol(bigintSubtypeBitand, Decl(bigintSubtypingTypeParameter.ts, 18, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 20, 29))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 20, 47))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 20, 29))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 20, 52))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 20, 29))

const bitand = a & b;
>bitand : Symbol(bitand, Decl(bigintSubtypingTypeParameter.ts, 21, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 20, 47))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 20, 52))

return bitand;
>bitand : Symbol(bitand, Decl(bigintSubtypingTypeParameter.ts, 21, 6))
}

function bigintSubtypeBitor<T extends bigint>(a: T, b: T): bigint {
>bigintSubtypeBitor : Symbol(bigintSubtypeBitor, Decl(bigintSubtypingTypeParameter.ts, 23, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 25, 28))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 25, 46))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 25, 28))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 25, 51))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 25, 28))

const bitor = a | b;
>bitor : Symbol(bitor, Decl(bigintSubtypingTypeParameter.ts, 26, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 25, 46))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 25, 51))

return bitor;
>bitor : Symbol(bitor, Decl(bigintSubtypingTypeParameter.ts, 26, 6))
}

function bigintSubtypeLeftshift<T extends bigint>(a: T, b: T): bigint {
>bigintSubtypeLeftshift : Symbol(bigintSubtypeLeftshift, Decl(bigintSubtypingTypeParameter.ts, 28, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 30, 32))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 30, 50))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 30, 32))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 30, 55))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 30, 32))

const leftshift = a << b;
>leftshift : Symbol(leftshift, Decl(bigintSubtypingTypeParameter.ts, 31, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 30, 50))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 30, 55))

return leftshift;
>leftshift : Symbol(leftshift, Decl(bigintSubtypingTypeParameter.ts, 31, 6))
}

function bigintSubtypeRightshift<T extends bigint>(a: T, b: T): bigint {
>bigintSubtypeRightshift : Symbol(bigintSubtypeRightshift, Decl(bigintSubtypingTypeParameter.ts, 33, 1))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 35, 33))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 35, 51))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 35, 33))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 35, 56))
>T : Symbol(T, Decl(bigintSubtypingTypeParameter.ts, 35, 33))

const rightshift = a >> b;
>rightshift : Symbol(rightshift, Decl(bigintSubtypingTypeParameter.ts, 36, 6))
>a : Symbol(a, Decl(bigintSubtypingTypeParameter.ts, 35, 51))
>b : Symbol(b, Decl(bigintSubtypingTypeParameter.ts, 35, 56))

return rightshift;
>rightshift : Symbol(rightshift, Decl(bigintSubtypingTypeParameter.ts, 36, 6))
}

Loading