diff --git a/crates/ty_python_semantic/resources/mdtest/literal/collections/list.md b/crates/ty_python_semantic/resources/mdtest/literal/collections/list.md index 15f385fa88..4e487a75a6 100644 --- a/crates/ty_python_semantic/resources/mdtest/literal/collections/list.md +++ b/crates/ty_python_semantic/resources/mdtest/literal/collections/list.md @@ -38,6 +38,21 @@ reveal_type(x[0].__name__) # revealed: Unknown | str reveal_type([1, (1, 2), (1, 2, 3)]) ``` +## Invariant generic elements + +We take care not to promote invariant generics: + +```py +from typing import Literal + +def _(a: list[Literal[1]], b: Literal[2]): + c = [a] + reveal_type(c) # revealed: list[Unknown | list[Literal[1]]] + + d = [(a, b)] + reveal_type(d) # revealed: list[Unknown | tuple[list[Literal[1]], int]] +``` + ## List comprehensions ```py diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 477bf9aa11..bffabd57f2 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -6648,7 +6648,7 @@ impl<'db> Type<'db> { tcx: TypeContext<'db>, visitor: &ApplyTypeMappingVisitor<'db>, ) -> Type<'db> { - match self { + let ty = match self { Type::TypeVar(bound_typevar) => match type_mapping { TypeMapping::Specialization(specialization) => { specialization.get(db, bound_typevar).unwrap_or(self) @@ -6837,6 +6837,15 @@ impl<'db> Type<'db> { | Type::BoundSuper(_) | Type::SpecialForm(_) | Type::KnownInstance(_) => self, + }; + + match type_mapping { + TypeMapping::PromoteLiterals => { + // It is only sound to promote to a supertype (i.e. it is unsound to promote + // invariant generics). + if self.is_subtype_of(db, ty) { ty } else { self } + } + _ => ty, } }