Skip to content

Commit

Permalink
Fixed a bug that resulted in incorrect type evaluation when calling a…
Browse files Browse the repository at this point in the history
… `tuple` constructor with bidirectional type inference and the value passed to the constructor is an `Iterable[Any]`. This addresses microsoft#7085. (microsoft#7155)
  • Loading branch information
erictraut committed Jan 29, 2024
1 parent 7362545 commit c8c8cea
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 6 deletions.
23 changes: 21 additions & 2 deletions packages/pyright-internal/src/analyzer/constraintSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,12 @@ export function assignTypeToTypeVar(
// There was previously no narrow bound. We've now established one.
newNarrowTypeBound = adjSrcType;
} else if (!isTypeSame(curNarrowTypeBound, adjSrcType, {}, recursionCount)) {
if (
if (isAnyOrUnknown(adjSrcType) && curEntry.tupleTypes) {
// Handle the tuple case specially. If Any or Unknown is assigned
// during the construction of a tuple, the resulting tuple type must
// be tuple[Any, ...], which is compatible with any tuple.
newNarrowTypeBound = adjSrcType;
} else if (
evaluator.assignType(
curNarrowTypeBound,
adjSrcType,
Expand Down Expand Up @@ -587,13 +592,25 @@ export function assignTypeToTypeVar(
}
}

// Update the tuple types based on the new type bounds. We need to
// switch to an unbounded tuple type since the length of the resulting
// tuple is indeterminate.
let newTupleTypes = curEntry?.tupleTypes;
if (newTupleTypes) {
const updatedType = newNarrowTypeBound ?? newWideTypeBound;
if (updatedType) {
newTupleTypes = [{ type: updatedType, isUnbounded: true }];
}
}

if (!typeVarContext.isLocked() && isTypeVarInScope) {
updateTypeVarType(
evaluator,
typeVarContext,
destType,
newNarrowTypeBound,
newWideTypeBound,
newTupleTypes,
(flags & (AssignTypeFlags.PopulatingExpectedType | AssignTypeFlags.RetainLiteralsForTypeVar)) !== 0
);
}
Expand All @@ -619,6 +636,7 @@ export function updateTypeVarType(
destType: TypeVarType,
narrowTypeBound: Type | undefined,
wideTypeBound: Type | undefined,
tupleTypes: TupleTypeArgument[] | undefined = undefined,
forceRetainLiterals = false
) {
let narrowTypeBoundNoLiterals: Type | undefined;
Expand All @@ -637,7 +655,7 @@ export function updateTypeVarType(
}
}

typeVarContext.setTypeVarType(destType, narrowTypeBound, narrowTypeBoundNoLiterals, wideTypeBound);
typeVarContext.setTypeVarType(destType, narrowTypeBound, narrowTypeBoundNoLiterals, wideTypeBound, tupleTypes);
}

function assignTypeToConstrainedTypeVar(
Expand Down Expand Up @@ -842,6 +860,7 @@ function assignTypeToConstrainedTypeVar(
destType,
constrainedType,
curWideTypeBound,
/* tupleTypes */ undefined,
forceRetainLiterals
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22419,6 +22419,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
typeParam,
variance !== Variance.Contravariant ? typeArgType : undefined,
variance !== Variance.Covariant ? typeArgType : undefined,
/* tupleTypes */ curSrcType.tupleTypeArguments,
/* forceRetainLiterals */ true
);
}
Expand Down
16 changes: 12 additions & 4 deletions packages/pyright-internal/src/analyzer/typeVarContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,17 @@ export class TypeVarSignatureContext {
reference: TypeVarType,
narrowBound: Type | undefined,
narrowBoundNoLiterals?: Type,
wideBound?: Type
wideBound?: Type,
tupleTypes?: TupleTypeArgument[]
) {
const key = TypeVarType.getNameWithScope(reference);
this._typeVarMap.set(key, { typeVar: reference, narrowBound, narrowBoundNoLiterals, wideBound });
this._typeVarMap.set(key, {
typeVar: reference,
narrowBound,
narrowBoundNoLiterals,
wideBound,
tupleTypes,
});
}

getTupleTypeVar(reference: TypeVarType): TupleTypeArgument[] | undefined {
Expand Down Expand Up @@ -450,12 +457,13 @@ export class TypeVarContext {
reference: TypeVarType,
narrowBound: Type | undefined,
narrowBoundNoLiterals?: Type,
wideBound?: Type
wideBound?: Type,
tupleTypes?: TupleTypeArgument[]
) {
assert(!this._isLocked);

return this._signatureContexts.forEach((context) => {
context.setTypeVarType(reference, narrowBound, narrowBoundNoLiterals, wideBound);
context.setTypeVarType(reference, narrowBound, narrowBoundNoLiterals, wideBound, tupleTypes);
});
}

Expand Down
7 changes: 7 additions & 0 deletions packages/pyright-internal/src/tests/samples/tuple18.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# This sample tests the case where the tuple constructor is called
# explicitly with bidirectional type inference.

from typing import Any, Iterable

# This should generate an error.
v1: tuple[float] = tuple([1.0, 2.0])

# This should generate an error.
v2: tuple[float] | tuple[float, float] = tuple([1.0, 2.0])

v3: tuple[float, ...] = tuple([1, 2])


def f(x: Iterable[Any], y: Iterable):
a: tuple[int, int] = tuple(x)
b: tuple[int, int] = tuple(y)

0 comments on commit c8c8cea

Please sign in to comment.