mirror of
https://github.com/astral-sh/ruff.git
synced 2026-10-28 10:45:37 +08:00
[ty] Evaluate single-predicate narrowing directly
This commit is contained in:
1 parent
fbf83e2268
commit
319a6193ca
3 files changed
+62
-7
No files matched your search
@@ -1447,6 +1447,11 @@ pub struct BindingWithConstraintsIterator<'map, 'db> {
|
||||
}
|
||||
|
||||
impl<'map, 'db> BindingWithConstraintsIterator<'map, 'db> {
|
||||
/// Whether only one live binding remains, apart from an initial unbound sentinel.
|
||||
pub fn has_single_live_binding(&self) -> bool {
|
||||
self.inner.len() == 1
|
||||
}
|
||||
|
||||
pub const fn predicates(&self) -> &'map Predicates<'db> {
|
||||
&self.constraint_tables.predicates
|
||||
}
|
||||
|
||||
@@ -1830,6 +1830,7 @@ fn place_from_bindings_impl<'db>(
|
||||
let predicates = bindings_with_constraints.predicates();
|
||||
let reachability_constraints = bindings_with_constraints.reachability_constraints();
|
||||
let boundness_analysis = bindings_with_constraints.boundness_analysis();
|
||||
let single_binding = bindings_with_constraints.has_single_live_binding();
|
||||
let mut bindings_with_constraints = bindings_with_constraints.peekable();
|
||||
|
||||
let is_non_exported = |binding: Definition<'db>| {
|
||||
@@ -2006,7 +2007,7 @@ fn place_from_bindings_impl<'db>(
|
||||
binding_ty,
|
||||
)
|
||||
})
|
||||
.narrow(constraint, binding_ty),
|
||||
.narrow(constraint, binding_ty, single_binding),
|
||||
};
|
||||
Some((narrowed, static_reachability))
|
||||
},
|
||||
|
||||
@@ -592,6 +592,13 @@ const NON_TERMINAL_CALL_CHUNK_SIZE: usize = 16;
|
||||
const REACHABILITY_EVALUATION_CHUNK_SIZE: usize = 256;
|
||||
const CONTROL_FLOW_REACHABILITY_CHECKPOINT_INTERVAL: usize = 16;
|
||||
const NARROWING_EVALUATION_CHECKPOINT_INTERVAL: usize = 8;
|
||||
|
||||
fn is_narrowing_checkpoint_position(predicate: ScopedPredicateId) -> bool {
|
||||
let index = predicate.index();
|
||||
let position = index ^ (index / NARROWING_EVALUATION_CHECKPOINT_INTERVAL);
|
||||
(position + 1).is_multiple_of(NARROWING_EVALUATION_CHECKPOINT_INTERVAL)
|
||||
}
|
||||
|
||||
fn predicate_scope<'db>(db: &'db dyn Db, predicate: &Predicate<'db>) -> ScopeId<'db> {
|
||||
match predicate.node {
|
||||
PredicateNode::Expression(expression)
|
||||
@@ -978,7 +985,7 @@ pub(crate) fn narrow_type_by_constraint<'db>(
|
||||
place,
|
||||
base_ty,
|
||||
)
|
||||
.narrow(id, base_ty)
|
||||
.narrow(id, base_ty, true)
|
||||
}
|
||||
|
||||
fn apply_accumulated_narrowing<'db>(
|
||||
@@ -1173,6 +1180,7 @@ impl<'a, 'db> NarrowingProjector<'a, 'db> {
|
||||
&mut self,
|
||||
constraint: ScopedNarrowingConstraint,
|
||||
base_ty: Type<'db>,
|
||||
single_binding: bool,
|
||||
) -> Type<'db> {
|
||||
self.base_ty = base_ty;
|
||||
match constraint {
|
||||
@@ -1187,10 +1195,55 @@ impl<'a, 'db> NarrowingProjector<'a, 'db> {
|
||||
return base_ty;
|
||||
}
|
||||
|
||||
// With multiple bindings, registering each root can turn a suffix of another
|
||||
// binding's graph into a join. Replacement narrowing depends on those boundaries.
|
||||
if single_binding && let Some(ty) = self.narrow_single_predicate(constraint) {
|
||||
return ty;
|
||||
}
|
||||
|
||||
let root = self.project(constraint, true);
|
||||
self.narrow_projected(root, base_ty)
|
||||
}
|
||||
|
||||
/// Evaluates a single positive or negative expression predicate without projecting a graph.
|
||||
fn narrow_single_predicate(&self, root: ScopedNarrowingConstraint) -> Option<Type<'db>> {
|
||||
type Id = ScopedNarrowingConstraint;
|
||||
|
||||
let node = self.constraints.get_interior_node(root);
|
||||
let positive = match (node.if_true, node.if_uncertain, node.if_false) {
|
||||
(Id::ALWAYS_TRUE, Id::ALWAYS_FALSE, Id::ALWAYS_FALSE) => true,
|
||||
(Id::ALWAYS_FALSE, Id::ALWAYS_FALSE, Id::ALWAYS_TRUE) => false,
|
||||
_ => return None,
|
||||
};
|
||||
let predicate = self.predicates[node.atom];
|
||||
if !matches!(predicate.node, PredicateNode::Expression(_)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self
|
||||
.predicate_narrowing_targets
|
||||
.contains(node.atom, self.place)
|
||||
{
|
||||
if is_narrowing_checkpoint_position(node.atom) {
|
||||
return None;
|
||||
}
|
||||
let (pos, neg) = infer_narrowing_constraints(self.db, predicate, self.place);
|
||||
if pos.is_some() || neg.is_some() {
|
||||
return Some(apply_accumulated_narrowing(
|
||||
self.db,
|
||||
self.env,
|
||||
self.base_ty,
|
||||
if positive { pos } else { neg },
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let reachable = analyze_single(self.db, self.env, &predicate)
|
||||
.negate_if(!positive)
|
||||
.may_be_true();
|
||||
Some(if reachable { self.base_ty } else { Type::Never })
|
||||
}
|
||||
|
||||
/// Narrows a projected constraint while reusing suffix results for its original binding type.
|
||||
///
|
||||
/// Registering each root lets the graph recognize shared joins incrementally.
|
||||
@@ -1437,12 +1490,8 @@ impl<'a, 'db> NarrowingProjector<'a, 'db> {
|
||||
|
||||
let node = self.constraints.get_interior_node(id);
|
||||
let predicate = self.predicates[node.atom];
|
||||
let index = node.atom.index();
|
||||
let checkpoint_position =
|
||||
index ^ (index / NARROWING_EVALUATION_CHECKPOINT_INTERVAL);
|
||||
if (id != root || use_root_checkpoint)
|
||||
&& (checkpoint_position + 1)
|
||||
.is_multiple_of(NARROWING_EVALUATION_CHECKPOINT_INTERVAL)
|
||||
&& is_narrowing_checkpoint_position(node.atom)
|
||||
&& (self
|
||||
.predicate_narrowing_targets
|
||||
.contains(node.atom, self.place)
|
||||
|
||||
Reference in new issue
Block a user