mirror of https://github.com/astral-sh/ruff
here
This commit is contained in:
parent
705e4725ad
commit
a59fae85cc
|
|
@ -69,19 +69,21 @@
|
|||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Range;
|
||||
use std::ops::{Deref, DerefMut, Range};
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use salsa::plumbing::AsId;
|
||||
|
||||
use crate::types::generics::{GenericContext, InferableTypeVars, Specialization};
|
||||
use crate::types::generics::{
|
||||
GenericContext, InferableTypeVars, PartialSpecialization, Specialization,
|
||||
};
|
||||
use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard};
|
||||
use crate::types::{
|
||||
BoundTypeVarIdentity, BoundTypeVarInstance, IntersectionType, Type, TypeRelation,
|
||||
TypeVarBoundOrConstraints, UnionType, walk_bound_type_var_type,
|
||||
BoundTypeVarIdentity, BoundTypeVarInstance, IntersectionType, Type, TypeContext, TypeMapping,
|
||||
TypeRelation, TypeVarBoundOrConstraints, UnionType, walk_bound_type_var_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use crate::{Db, FxOrderMap, FxOrderSet};
|
||||
|
||||
/// An extension trait for building constraint sets from [`Option`] values.
|
||||
pub(crate) trait OptionConstraintsExtension<T> {
|
||||
|
|
@ -1094,6 +1096,14 @@ impl<'db> Node<'db> {
|
|||
self.implies(db, constraint)
|
||||
}
|
||||
|
||||
fn typevars(self, db: &'db dyn Db) -> FxHashSet<BoundTypeVarInstance<'db>> {
|
||||
let mut typevars = FxHashSet::default();
|
||||
self.for_each_constraint(db, &mut |constraint| {
|
||||
typevars.insert(constraint.typevar(db));
|
||||
});
|
||||
typevars
|
||||
}
|
||||
|
||||
fn satisfied_by_all_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
|
|
@ -1105,11 +1115,6 @@ impl<'db> Node<'db> {
|
|||
Node::Interior(_) => {}
|
||||
}
|
||||
|
||||
let mut typevars = FxHashSet::default();
|
||||
self.for_each_constraint(db, &mut |constraint| {
|
||||
typevars.insert(constraint.typevar(db));
|
||||
});
|
||||
|
||||
// Returns if some specialization satisfies this constraint set.
|
||||
let some_specialization_satisfies = move |specializations: Node<'db>| {
|
||||
let when_satisfied = specializations.implies(db, self).and(db, specializations);
|
||||
|
|
@ -1124,7 +1129,7 @@ impl<'db> Node<'db> {
|
|||
.is_always_satisfied(db)
|
||||
};
|
||||
|
||||
for typevar in typevars {
|
||||
for typevar in self.typevars(db) {
|
||||
if typevar.is_inferable(db, inferable) {
|
||||
// If the typevar is in inferable position, we need to verify that some valid
|
||||
// specialization satisfies the constraint set.
|
||||
|
|
@ -3354,6 +3359,219 @@ impl<'db> BoundTypeVarInstance<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct ConstraintSolutions<'db>(Vec<ConstraintSolution<'db>>);
|
||||
|
||||
impl<'db> Deref for ConstraintSolutions<'db> {
|
||||
type Target = Vec<ConstraintSolution<'db>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> DerefMut for ConstraintSolutions<'db> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'db> IntoIterator for &'a ConstraintSolutions<'db> {
|
||||
type Item = &'a ConstraintSolution<'db>;
|
||||
type IntoIter = std::slice::Iter<'a, ConstraintSolution<'db>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct ConstraintSolution<'db> {
|
||||
assignments: FxOrderMap<BoundTypeVarInstance<'db>, (Type<'db>, Type<'db>)>,
|
||||
}
|
||||
|
||||
impl<'db> ConstraintSolution<'db> {
|
||||
pub(crate) fn get_upper_bound(
|
||||
&self,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
) -> Option<Type<'db>> {
|
||||
self.assignments
|
||||
.get(&bound_typevar)
|
||||
.map(|(_, upper_bound)| *upper_bound)
|
||||
}
|
||||
|
||||
fn add_mapping(
|
||||
&mut self,
|
||||
db: &'db dyn Db,
|
||||
typevar: BoundTypeVarInstance<'db>,
|
||||
lower_bound: Type<'db>,
|
||||
upper_bound: Type<'db>,
|
||||
) {
|
||||
eprintln!(
|
||||
" -> ADD {} ≤ {} ≤ {}",
|
||||
lower_bound.display(db),
|
||||
typevar.identity(db).display(db),
|
||||
upper_bound.display(db),
|
||||
);
|
||||
let (existing_lower_bound, existing_upper_bound) = self
|
||||
.assignments
|
||||
.entry(typevar)
|
||||
.or_insert_with(|| (Type::Never, Type::object()));
|
||||
let new_lower_bound = UnionType::from_elements(db, [*existing_lower_bound, lower_bound]);
|
||||
let new_upper_bound =
|
||||
IntersectionType::from_elements(db, [*existing_upper_bound, upper_bound]);
|
||||
*existing_lower_bound = new_lower_bound;
|
||||
*existing_upper_bound = new_upper_bound;
|
||||
}
|
||||
|
||||
fn collapse_to_single_types(&mut self) {
|
||||
for (_, (lower_bound, upper_bound)) in &mut self.assignments {
|
||||
// Use the lower bound if it's more "interesting", otherwise use the upper bound.
|
||||
if upper_bound.is_object() && !lower_bound.is_never() {
|
||||
*upper_bound = *lower_bound;
|
||||
} else {
|
||||
*lower_bound = *upper_bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn close_over_typevars(&mut self, db: &'db dyn Db) {
|
||||
// We have to pull this out into a separate variable to satisfy the borrow checker.
|
||||
let typevars: Vec<_> = self.assignments.keys().copied().collect();
|
||||
loop {
|
||||
let mut any_changed = false;
|
||||
for bound_typevar in &typevars {
|
||||
let (existing_lower, existing_upper) = self.assignments[bound_typevar];
|
||||
let updated_lower = existing_lower.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::PartialSpecialization(
|
||||
PartialSpecialization::FromConstraintSolution(self),
|
||||
),
|
||||
TypeContext::default(),
|
||||
);
|
||||
let updated_upper = existing_upper.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::PartialSpecialization(
|
||||
PartialSpecialization::FromConstraintSolution(self),
|
||||
),
|
||||
TypeContext::default(),
|
||||
);
|
||||
if updated_lower != existing_lower || updated_upper != existing_upper {
|
||||
self.assignments[bound_typevar] = (updated_lower, updated_upper);
|
||||
any_changed = true;
|
||||
}
|
||||
}
|
||||
if !any_changed {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn display(&self, db: &'db dyn Db) -> impl Display {
|
||||
struct DisplayConstraintSolution<'a, 'db> {
|
||||
solution: &'a ConstraintSolution<'db>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl Display for DisplayConstraintSolution<'_, '_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"[{}]",
|
||||
self.solution.assignments.iter().format_with(
|
||||
", ",
|
||||
|(bound_typevar, (lower, upper)), f| if lower == upper {
|
||||
f(&format_args!(
|
||||
"{} = {}",
|
||||
bound_typevar.identity(self.db).display(self.db),
|
||||
lower.display(self.db)
|
||||
))
|
||||
} else {
|
||||
f(&format_args!(
|
||||
"{} ≤ {} ≤ {}",
|
||||
lower.display(self.db),
|
||||
bound_typevar.identity(self.db).display(self.db),
|
||||
upper.display(self.db),
|
||||
))
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DisplayConstraintSolution { solution: self, db }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'db> IntoIterator for &'a ConstraintSolution<'db> {
|
||||
type Item = (&'a BoundTypeVarInstance<'db>, &'a (Type<'db>, Type<'db>));
|
||||
type IntoIter = ordermap::map::Iter<'a, BoundTypeVarInstance<'db>, (Type<'db>, Type<'db>)>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.assignments.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> ConstraintSet<'db> {
|
||||
pub(crate) fn solve_for(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
inferable: InferableTypeVars<'_, 'db>,
|
||||
) -> ConstraintSolutions<'db> {
|
||||
let mut solutions = ConstraintSolutions::default();
|
||||
self.for_each_path(db, |path| {
|
||||
eprintln!(
|
||||
" -> PATH [{}]",
|
||||
path.assignments
|
||||
.iter()
|
||||
.format_with(", ", |assignment, f| f(&assignment.display(db)))
|
||||
);
|
||||
let mut solution = ConstraintSolution::default();
|
||||
for constraint in path.positive_constraints() {
|
||||
let typevar = constraint.typevar(db);
|
||||
let lower = constraint.lower(db);
|
||||
let upper = constraint.upper(db);
|
||||
eprintln!(
|
||||
" ~> constraint {} ≤ {} ≤ {}",
|
||||
lower.display(db),
|
||||
typevar.identity(db).display(db),
|
||||
upper.display(db)
|
||||
);
|
||||
if typevar.is_inferable(db, inferable) {
|
||||
eprintln!(" ~> add1");
|
||||
solution.add_mapping(db, typevar, lower, upper);
|
||||
}
|
||||
if let Type::TypeVar(lower_bound_typevar) = lower
|
||||
&& lower_bound_typevar.is_inferable(db, inferable)
|
||||
{
|
||||
eprintln!(" ~> add2");
|
||||
solution.add_mapping(
|
||||
db,
|
||||
lower_bound_typevar,
|
||||
Type::Never,
|
||||
Type::TypeVar(typevar),
|
||||
);
|
||||
}
|
||||
if let Type::TypeVar(upper_bound_typevar) = upper
|
||||
&& upper_bound_typevar.is_inferable(db, inferable)
|
||||
{
|
||||
eprintln!(" ~> add3");
|
||||
solution.add_mapping(
|
||||
db,
|
||||
upper_bound_typevar,
|
||||
Type::TypeVar(typevar),
|
||||
Type::object(),
|
||||
);
|
||||
}
|
||||
}
|
||||
solution.collapse_to_single_types();
|
||||
solution.close_over_typevars(db);
|
||||
solutions.push(solution);
|
||||
});
|
||||
solutions
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> GenericContext<'db> {
|
||||
pub(crate) fn specialize_constrained(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind, ScopeId};
|
|||
use crate::semantic_index::{SemanticIndex, semantic_index};
|
||||
use crate::types::class::ClassType;
|
||||
use crate::types::class_base::ClassBase;
|
||||
use crate::types::constraints::ConstraintSet;
|
||||
use crate::types::constraints::{ConstraintSet, ConstraintSolution};
|
||||
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
||||
use crate::types::signatures::Parameters;
|
||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||
|
|
@ -1304,6 +1304,7 @@ pub enum PartialSpecialization<'a, 'db> {
|
|||
generic_context: GenericContext<'db>,
|
||||
types: &'a [Type<'db>],
|
||||
},
|
||||
FromConstraintSolution(&'a ConstraintSolution<'db>),
|
||||
}
|
||||
|
||||
impl<'db> PartialSpecialization<'_, 'db> {
|
||||
|
|
@ -1324,6 +1325,9 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
|||
.get_index_of(&bound_typevar.identity(db))?;
|
||||
types.get(index).copied()
|
||||
}
|
||||
PartialSpecialization::FromConstraintSolution(solution) => {
|
||||
solution.get_upper_bound(bound_typevar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1661,6 +1665,7 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
(Type::Callable(formal_callable), _) => {
|
||||
eprintln!("==> {}", formal.display(self.db));
|
||||
eprintln!(" {}", actual.display(self.db));
|
||||
eprintln!(" {}", self.inferable.display(self.db));
|
||||
let Some(actual_callables) = actual.try_upcast_to_callable(self.db) else {
|
||||
eprintln!(" -> NOPE");
|
||||
return Ok(());
|
||||
|
|
@ -1688,42 +1693,13 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
eprintln!(" {}", when.display(self.db));
|
||||
eprintln!(" {}", when.display_graph(self.db, &" "));
|
||||
|
||||
when.for_each_path(self.db, |path| {
|
||||
eprintln!(
|
||||
"--> path [{}]",
|
||||
path.positive_constraints()
|
||||
.map(|c| c.display(self.db))
|
||||
.format(", ")
|
||||
);
|
||||
for constraint in path.positive_constraints() {
|
||||
let typevar = constraint.typevar(self.db);
|
||||
let lower = constraint.lower(self.db);
|
||||
let upper = constraint.upper(self.db);
|
||||
let upper_has_noninferable_typevar = any_over_type(
|
||||
self.db,
|
||||
upper,
|
||||
&|ty| {
|
||||
ty.as_typevar().is_some_and(|bound_typevar| {
|
||||
!bound_typevar.is_inferable(self.db, self.inferable)
|
||||
})
|
||||
},
|
||||
false,
|
||||
);
|
||||
if !upper.is_object() && !upper_has_noninferable_typevar {
|
||||
self.add_type_mapping(typevar, upper, polarity, &mut f);
|
||||
}
|
||||
if typevar.is_inferable(self.db, self.inferable)
|
||||
&& let Type::TypeVar(lower_bound_typevar) = lower
|
||||
{
|
||||
self.add_type_mapping(
|
||||
lower_bound_typevar,
|
||||
Type::TypeVar(typevar),
|
||||
polarity,
|
||||
&mut f,
|
||||
);
|
||||
let solutions = when.solve_for(self.db, self.inferable);
|
||||
for solution in &solutions {
|
||||
eprintln!("--> solution [{}]", solution.display(self.db));
|
||||
for (bound_typevar, (_, ty)) in solution {
|
||||
self.add_type_mapping(*bound_typevar, *ty, polarity, &mut f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Add more forms that we can structurally induct into: type[C], callables
|
||||
|
|
|
|||
|
|
@ -752,7 +752,8 @@ impl<'db> Signature<'db> {
|
|||
// we produce, we reduce it back down to the inferable set that the caller asked about.
|
||||
// If we introduced new inferable typevars, those will be existentially quantified away
|
||||
// before returning.
|
||||
when.reduce_inferable(db, self_inferable.iter().chain(other_inferable.iter()))
|
||||
//when.reduce_inferable(db, self_inferable.iter().chain(other_inferable.iter()))
|
||||
when
|
||||
}
|
||||
|
||||
fn is_equivalent_to_inner(
|
||||
|
|
@ -898,7 +899,8 @@ impl<'db> Signature<'db> {
|
|||
// we produce, we reduce it back down to the inferable set that the caller asked about.
|
||||
// If we introduced new inferable typevars, those will be existentially quantified away
|
||||
// before returning.
|
||||
when.reduce_inferable(db, self_inferable.iter().chain(other_inferable.iter()))
|
||||
//when.reduce_inferable(db, self_inferable.iter().chain(other_inferable.iter()))
|
||||
when
|
||||
}
|
||||
|
||||
fn has_relation_to_inner(
|
||||
|
|
|
|||
Loading…
Reference in New Issue