Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,7 @@ e: list[Any] | None = [1]
reveal_type(e) # revealed: list[Any]

f: list[Any] | None = f2(1)
# TODO: Better constraint solver.
reveal_type(f) # revealed: list[int] | None
reveal_type(f) # revealed: list[Any] | None

g: list[Any] | dict[Any, Any] = f3(1)
# TODO: Better constraint solver.
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/resources/mdtest/promotion.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ x11: list[Literal[1] | Literal[2] | Literal[3]] = [1, 2, 3]
reveal_type(x11) # revealed: list[Literal[1, 2, 3]]

x12: Y[Y[Literal[1]]] = [[1]]
reveal_type(x12) # revealed: list[Y[Literal[1]]]
reveal_type(x12) # revealed: list[list[Literal[1]]]

x13: list[tuple[Literal[1], Literal[2], Literal[3]]] = [(1, 2, 3)]
reveal_type(x13) # revealed: list[tuple[Literal[1], Literal[2], Literal[3]]]
Expand Down
69 changes: 40 additions & 29 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use ruff_diagnostics::{Edit, Fix};
use rustc_hash::FxHashMap;

use std::borrow::Cow;
use std::cell::RefCell;
use std::time::Duration;

use bitflags::bitflags;
Expand Down Expand Up @@ -50,7 +49,7 @@ use crate::types::bound_super::BoundSuperType;
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
pub(crate) use crate::types::callable::{CallableType, CallableTypes};
pub(crate) use crate::types::class_base::ClassBase;
use crate::types::constraints::ConstraintSetBuilder;
use crate::types::constraints::{ConstraintSetBuilder, Solutions};
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
use crate::types::diagnostic::{INVALID_AWAIT, INVALID_TYPE_FORM};
pub use crate::types::display::{DisplaySettings, TypeDetail, TypeDisplayDetails};
Expand All @@ -59,10 +58,10 @@ use crate::types::function::{
DataclassTransformerFlags, DataclassTransformerParams, FunctionDecorators, FunctionSpans,
FunctionType, KnownFunction,
};
pub(crate) use crate::types::generics::GenericContext;
use crate::types::generics::{
ApplySpecialization, InferableTypeVars, Specialization, bind_typevar,
};
pub(crate) use crate::types::generics::{GenericContext, SpecializationBuilder};
use crate::types::infer::InferenceFlags;
use crate::types::known_instance::{InternedConstraintSet, InternedType, UnionTypeInstance};
pub use crate::types::method::{BoundMethodType, KnownBoundMethodType, WrapperDescriptorKind};
Expand Down Expand Up @@ -1903,17 +1902,44 @@ impl<'db> Type<'db> {
let generic_context = specialization.generic_context(db);

// Collect the type mappings used to narrow the type context.
let tcx_mappings = {
let mut builder =
SpecializationBuilder::new(db, generic_context.inferable_typevars(db));

if let Some(tcx) = tcx.annotation {
//
// We use a forward CSA check (`alias_instance ≤ tcx`) to infer what each typevar
// in the identity specialization maps to in the type context. For example, if
// `tcx = list[int]` and `alias_instance = list[T]`, the CSA produces `T = int`.
let tcx_mappings: FxHashMap<_, _> = tcx
.annotation
.and_then(|tcx| {
let alias_instance = Type::instance(db, class_literal.identity_specialization(db));
let _ = builder.infer_reverse(constraints, tcx, alias_instance);
}

builder.into_type_mappings()
};
let inferable = generic_context.inferable_typevars(db);
let set = alias_instance.when_constraint_set_assignable_to(
db,
tcx,
constraints,
inferable,
);
match set.solutions(db, constraints) {
Solutions::Constrained(solutions) => {
let mut mappings = FxHashMap::default();
for solution in solutions.iter() {
for binding in solution {
mappings
.entry(binding.bound_typevar.identity(db))
.and_modify(|existing| {
*existing = UnionType::from_two_elements(
db,
*existing,
binding.solution,
);
})
.or_insert(binding.solution);
}
}
Some(mappings)
}
_ => None,
}
})
.unwrap_or_default();

for (type_var, ty) in generic_context.variables(db).zip(specialization.types(db)) {
let variance = type_var.variance_with_polarity(db, polarity);
Expand Down Expand Up @@ -5363,12 +5389,6 @@ impl<'db> Type<'db> {
match type_mapping {
TypeMapping::EagerExpansion => unreachable!("handled above"),

// For UniqueSpecialization, get raw value type, apply specialization, then apply mapping.
TypeMapping::UniqueSpecialization { .. } => {
let value_type = alias.raw_value_type(db);
alias.apply_function_specialization(db, value_type).apply_type_mapping_impl(db, type_mapping, tcx, visitor)
}

_ => {
let value_type = alias.raw_value_type(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor);
alias.apply_function_specialization(db, value_type).apply_type_mapping_impl(db, type_mapping, tcx, visitor)
Expand All @@ -5393,7 +5413,6 @@ impl<'db> Type<'db> {
Type::LiteralValue(_) => match type_mapping {
TypeMapping::ApplySpecialization(_) |
TypeMapping::ApplySpecializationWithMaterialization { .. } |
TypeMapping::UniqueSpecialization { .. } |
TypeMapping::BindLegacyTypevars(_) |
TypeMapping::BindSelf { .. } |
TypeMapping::ReplaceSelf { .. } |
Expand All @@ -5408,7 +5427,6 @@ impl<'db> Type<'db> {
Type::Dynamic(_) => match type_mapping {
TypeMapping::ApplySpecialization(_) |
TypeMapping::ApplySpecializationWithMaterialization { .. } |
TypeMapping::UniqueSpecialization { .. } |
TypeMapping::BindLegacyTypevars(_) |
TypeMapping::BindSelf(..) |
TypeMapping::ReplaceSelf { .. } |
Expand Down Expand Up @@ -6181,11 +6199,6 @@ pub enum TypeMapping<'a, 'db> {
specialization: ApplySpecialization<'a, 'db>,
materialization_kind: MaterializationKind,
},
/// Resets any specializations to contain unique synthetic type variables.
UniqueSpecialization {
// A list of synthetic type variables, and the types they replaced.
specialization: RefCell<Vec<(BoundTypeVarInstance<'db>, Type<'db>)>>,
},
/// Replaces any literal types with their corresponding promoted type form (e.g. `Literal["string"]`
/// to `str`, or `def _() -> int` to `Callable[[], int]`).
Promote(PromotionMode),
Expand Down Expand Up @@ -6237,8 +6250,7 @@ impl<'db> TypeMapping<'_, 'db> {
}),
)
}
TypeMapping::UniqueSpecialization { .. }
| TypeMapping::Promote(_)
TypeMapping::Promote(_)
| TypeMapping::BindLegacyTypevars(_)
| TypeMapping::Materialize(_)
| TypeMapping::ReplaceParameterDefaults
Expand Down Expand Up @@ -6283,7 +6295,6 @@ impl<'db> TypeMapping<'_, 'db> {
},
TypeMapping::Promote(mode) => TypeMapping::Promote(mode.flip()),
TypeMapping::ApplySpecialization(_)
| TypeMapping::UniqueSpecialization { .. }
| TypeMapping::BindLegacyTypevars(_)
| TypeMapping::BindSelf(..)
| TypeMapping::ReplaceSelf { .. }
Expand Down
Loading