mirror of https://github.com/astral-sh/ruff
use source order in specialize_constrained too
This commit is contained in:
parent
1f34f43745
commit
7e2ea8bd69
|
|
@ -78,8 +78,8 @@ use salsa::plumbing::AsId;
|
||||||
use crate::types::generics::{GenericContext, InferableTypeVars, Specialization};
|
use crate::types::generics::{GenericContext, InferableTypeVars, Specialization};
|
||||||
use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard};
|
use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundTypeVarIdentity, BoundTypeVarInstance, IntersectionBuilder, IntersectionType, Type,
|
BoundTypeVarIdentity, BoundTypeVarInstance, IntersectionType, Type, TypeVarBoundOrConstraints,
|
||||||
TypeVarBoundOrConstraints, UnionBuilder, UnionType, walk_bound_type_var_type,
|
UnionType, walk_bound_type_var_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
|
@ -1318,7 +1318,7 @@ impl<'db> Node<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
bound_typevar: BoundTypeVarIdentity<'db>,
|
bound_typevar: BoundTypeVarIdentity<'db>,
|
||||||
mut f: impl FnMut(Option<(Type<'db>, Type<'db>)>),
|
mut f: impl FnMut(Option<(Type<'db>, Type<'db>, usize)>),
|
||||||
) {
|
) {
|
||||||
self.retain_one(db, bound_typevar)
|
self.retain_one(db, bound_typevar)
|
||||||
.find_representative_types_inner(db, &mut Vec::default(), &mut f);
|
.find_representative_types_inner(db, &mut Vec::default(), &mut f);
|
||||||
|
|
@ -1328,7 +1328,7 @@ impl<'db> Node<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
current_bounds: &mut Vec<RepresentativeBounds<'db>>,
|
current_bounds: &mut Vec<RepresentativeBounds<'db>>,
|
||||||
f: &mut dyn FnMut(Option<(Type<'db>, Type<'db>)>),
|
f: &mut dyn FnMut(Option<(Type<'db>, Type<'db>, usize)>),
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Node::AlwaysTrue => {
|
Node::AlwaysTrue => {
|
||||||
|
|
@ -1354,10 +1354,17 @@ impl<'db> Node<'db> {
|
||||||
// process.
|
// process.
|
||||||
debug_assert!(greatest_lower_bound.is_assignable_to(db, least_upper_bound));
|
debug_assert!(greatest_lower_bound.is_assignable_to(db, least_upper_bound));
|
||||||
|
|
||||||
|
// SAFETY: Checked that current_bounds is non-empty above.
|
||||||
|
let minimum_source_order = current_bounds[0].source_order;
|
||||||
|
|
||||||
// We've been tracking the lower and upper bound that the types for this path must
|
// We've been tracking the lower and upper bound that the types for this path must
|
||||||
// satisfy. Pass those bounds along and let the caller choose a representative type
|
// satisfy. Pass those bounds along and let the caller choose a representative type
|
||||||
// from within that range.
|
// from within that range.
|
||||||
f(Some((greatest_lower_bound, least_upper_bound)));
|
f(Some((
|
||||||
|
greatest_lower_bound,
|
||||||
|
least_upper_bound,
|
||||||
|
minimum_source_order,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::AlwaysFalse => {
|
Node::AlwaysFalse => {
|
||||||
|
|
@ -3617,6 +3624,7 @@ impl<'db> GenericContext<'db> {
|
||||||
|
|
||||||
// Then we find all of the "representative types" for each typevar in the constraint set.
|
// Then we find all of the "representative types" for each typevar in the constraint set.
|
||||||
let mut error_occurred = false;
|
let mut error_occurred = false;
|
||||||
|
let mut constraints = Vec::new();
|
||||||
let types = self.variables(db).map(|bound_typevar| {
|
let types = self.variables(db).map(|bound_typevar| {
|
||||||
// Each representative type represents one of the ways that the typevar can satisfy the
|
// Each representative type represents one of the ways that the typevar can satisfy the
|
||||||
// constraint, expressed as a lower/upper bound on the types that the typevar can
|
// constraint, expressed as a lower/upper bound on the types that the typevar can
|
||||||
|
|
@ -3628,10 +3636,7 @@ impl<'db> GenericContext<'db> {
|
||||||
// (This happens most often with constrained typevars.) We could in the future turn
|
// (This happens most often with constrained typevars.) We could in the future turn
|
||||||
// _each_ of the paths into separate specializations, but it's not clear what we would
|
// _each_ of the paths into separate specializations, but it's not clear what we would
|
||||||
// do with that, so instead we just report the ambiguity as a specialization failure.
|
// do with that, so instead we just report the ambiguity as a specialization failure.
|
||||||
let mut satisfied = false;
|
|
||||||
let mut unconstrained = false;
|
let mut unconstrained = false;
|
||||||
let mut greatest_lower_bound = UnionBuilder::new(db).order_elements(true);
|
|
||||||
let mut least_upper_bound = IntersectionBuilder::new(db).order_elements(true);
|
|
||||||
let identity = bound_typevar.identity(db);
|
let identity = bound_typevar.identity(db);
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
||||||
|
|
@ -3639,10 +3644,9 @@ impl<'db> GenericContext<'db> {
|
||||||
abstracted = %abstracted.retain_one(db, identity).display(db),
|
abstracted = %abstracted.retain_one(db, identity).display(db),
|
||||||
"find specialization for typevar",
|
"find specialization for typevar",
|
||||||
);
|
);
|
||||||
abstracted.find_representative_types(db, identity, |bounds| {
|
constraints.clear();
|
||||||
satisfied = true;
|
abstracted.find_representative_types(db, identity, |bounds| match bounds {
|
||||||
match bounds {
|
Some(bounds @ (lower_bound, upper_bound, _)) => {
|
||||||
Some((lower_bound, upper_bound)) => {
|
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
||||||
bound_typevar = %identity.display(db),
|
bound_typevar = %identity.display(db),
|
||||||
|
|
@ -3650,28 +3654,13 @@ impl<'db> GenericContext<'db> {
|
||||||
upper_bound = %upper_bound.display(db),
|
upper_bound = %upper_bound.display(db),
|
||||||
"found representative type",
|
"found representative type",
|
||||||
);
|
);
|
||||||
greatest_lower_bound.add_in_place(lower_bound);
|
constraints.push(bounds);
|
||||||
least_upper_bound.add_positive_in_place(upper_bound);
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unconstrained = true;
|
unconstrained = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// If there are no satisfiable paths in the BDD, then there is no valid specialization
|
|
||||||
// for this constraint set.
|
|
||||||
if !satisfied {
|
|
||||||
// TODO: Construct a useful error here
|
|
||||||
tracing::debug!(
|
|
||||||
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
|
||||||
bound_typevar = %identity.display(db),
|
|
||||||
"typevar cannot be satisfied",
|
|
||||||
);
|
|
||||||
error_occurred = true;
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The BDD is satisfiable, but the typevar is unconstrained, then we use `None` to tell
|
// The BDD is satisfiable, but the typevar is unconstrained, then we use `None` to tell
|
||||||
// specialize_recursive to fall back on the typevar's default.
|
// specialize_recursive to fall back on the typevar's default.
|
||||||
if unconstrained {
|
if unconstrained {
|
||||||
|
|
@ -3683,10 +3672,25 @@ impl<'db> GenericContext<'db> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are no satisfiable paths in the BDD, then there is no valid specialization
|
||||||
|
// for this constraint set.
|
||||||
|
if constraints.is_empty() {
|
||||||
|
// TODO: Construct a useful error here
|
||||||
|
tracing::debug!(
|
||||||
|
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
||||||
|
bound_typevar = %identity.display(db),
|
||||||
|
"typevar cannot be satisfied",
|
||||||
|
);
|
||||||
|
error_occurred = true;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// If `lower ≰ upper`, then there is no type that satisfies all of the paths in the
|
// If `lower ≰ upper`, then there is no type that satisfies all of the paths in the
|
||||||
// BDD. That's an ambiguous specialization, as described above.
|
// BDD. That's an ambiguous specialization, as described above.
|
||||||
let greatest_lower_bound = greatest_lower_bound.build();
|
let greatest_lower_bound =
|
||||||
let least_upper_bound = least_upper_bound.build();
|
UnionType::from_elements(db, constraints.iter().map(|(lower, _, _)| *lower));
|
||||||
|
let least_upper_bound =
|
||||||
|
IntersectionType::from_elements(db, constraints.iter().map(|(_, upper, _)| *upper));
|
||||||
if !greatest_lower_bound.is_assignable_to(db, least_upper_bound) {
|
if !greatest_lower_bound.is_assignable_to(db, least_upper_bound) {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue