[red-knot] Rename constraint to predicate (#16382)

In https://github.com/astral-sh/ruff/pull/16306#discussion_r1966290700,
@carljm pointed out that #16306 introduced a terminology problem, with
too many things called a "constraint". This is a follow-up PR that
renames `Constraint` to `Predicate` to hopefully clear things up a bit.
So now we have that:

- a _predicate_ is a Python expression that might influence type
inference
- a _narrowing constraint_ is a list of predicates that constraint the
type of a binding that is visible at a use
- a _visibility constraint_ is a ternary formula of predicates that
define whether a binding is visible or a statement is reachable

This is a pure renaming, with no behavioral changes.
This commit is contained in:
Douglas Creager 2025-02-25 14:52:40 -05:00 committed by GitHub
parent 86b01d2d3c
commit b39a4ad01d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 327 additions and 314 deletions

View file

@ -25,10 +25,10 @@ use crate::Db;
pub mod ast_ids; pub mod ast_ids;
pub mod attribute_assignment; pub mod attribute_assignment;
mod builder; mod builder;
pub(crate) mod constraint;
pub mod definition; pub mod definition;
pub mod expression; pub mod expression;
mod narrowing_constraints; mod narrowing_constraints;
pub(crate) mod predicate;
pub mod symbol; pub mod symbol;
mod use_def; mod use_def;
mod visibility_constraints; mod visibility_constraints;

View file

@ -15,9 +15,6 @@ use crate::module_name::ModuleName;
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey; use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
use crate::semantic_index::ast_ids::AstIdsBuilder; use crate::semantic_index::ast_ids::AstIdsBuilder;
use crate::semantic_index::attribute_assignment::{AttributeAssignment, AttributeAssignments}; use crate::semantic_index::attribute_assignment::{AttributeAssignment, AttributeAssignments};
use crate::semantic_index::constraint::{
Constraint, ConstraintNode, PatternConstraint, PatternConstraintKind, ScopedConstraintId,
};
use crate::semantic_index::definition::{ use crate::semantic_index::definition::{
AssignmentDefinitionNodeRef, ComprehensionDefinitionNodeRef, Definition, DefinitionCategory, AssignmentDefinitionNodeRef, ComprehensionDefinitionNodeRef, Definition, DefinitionCategory,
DefinitionNodeKey, DefinitionNodeRef, ExceptHandlerDefinitionNodeRef, ForStmtDefinitionNodeRef, DefinitionNodeKey, DefinitionNodeRef, ExceptHandlerDefinitionNodeRef, ForStmtDefinitionNodeRef,
@ -25,6 +22,9 @@ use crate::semantic_index::definition::{
WithItemDefinitionNodeRef, WithItemDefinitionNodeRef,
}; };
use crate::semantic_index::expression::{Expression, ExpressionKind}; use crate::semantic_index::expression::{Expression, ExpressionKind};
use crate::semantic_index::predicate::{
PatternPredicate, PatternPredicateKind, Predicate, PredicateNode, ScopedPredicateId,
};
use crate::semantic_index::symbol::{ use crate::semantic_index::symbol::{
FileScopeId, NodeWithScopeKey, NodeWithScopeRef, Scope, ScopeId, ScopeKind, ScopedSymbolId, FileScopeId, NodeWithScopeKey, NodeWithScopeRef, Scope, ScopeId, ScopeKind, ScopedSymbolId,
SymbolTableBuilder, SymbolTableBuilder,
@ -385,50 +385,60 @@ impl<'db> SemanticIndexBuilder<'db> {
definition definition
} }
fn record_expression_constraint(&mut self, constraint_node: &ast::Expr) -> Constraint<'db> { fn record_expression_narrowing_constraint(
let constraint = self.build_constraint(constraint_node); &mut self,
self.record_constraint(constraint); precide_node: &ast::Expr,
constraint ) -> Predicate<'db> {
let predicate = self.build_predicate(precide_node);
self.record_narrowing_constraint(predicate);
predicate
} }
fn build_constraint(&mut self, constraint_node: &ast::Expr) -> Constraint<'db> { fn build_predicate(&mut self, predicate_node: &ast::Expr) -> Predicate<'db> {
let expression = self.add_standalone_expression(constraint_node); let expression = self.add_standalone_expression(predicate_node);
Constraint { Predicate {
node: ConstraintNode::Expression(expression), node: PredicateNode::Expression(expression),
is_positive: true, is_positive: true,
} }
} }
/// Adds a new constraint to the list of all constraints, but does not record it. Returns the /// Adds a new predicate to the list of all predicates, but does not record it. Returns the
/// constraint ID for later recording using [`SemanticIndexBuilder::record_constraint_id`]. /// predicate ID for later recording using
fn add_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId { /// [`SemanticIndexBuilder::record_narrowing_constraint_id`].
self.current_use_def_map_mut().add_constraint(constraint) fn add_predicate(&mut self, predicate: Predicate<'db>) -> ScopedPredicateId {
self.current_use_def_map_mut().add_predicate(predicate)
} }
/// Negates a constraint and adds it to the list of all constraints, does not record it. /// Negates a predicate and adds it to the list of all predicates, does not record it.
fn add_negated_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId { fn add_negated_predicate(&mut self, predicate: Predicate<'db>) -> ScopedPredicateId {
let negated = Constraint { let negated = Predicate {
node: constraint.node, node: predicate.node,
is_positive: false, is_positive: false,
}; };
self.current_use_def_map_mut().add_constraint(negated) self.current_use_def_map_mut().add_predicate(negated)
} }
/// Records a previously added constraint by adding it to all live bindings. /// Records a previously added narrowing constraint by adding it to all live bindings.
fn record_constraint_id(&mut self, constraint: ScopedConstraintId) { fn record_narrowing_constraint_id(&mut self, predicate: ScopedPredicateId) {
self.current_use_def_map_mut() self.current_use_def_map_mut()
.record_constraint_id(constraint); .record_narrowing_constraint(predicate);
} }
/// Adds and records a constraint, i.e. adds it to all live bindings. /// Adds and records a narrowing constraint, i.e. adds it to all live bindings.
fn record_constraint(&mut self, constraint: Constraint<'db>) { fn record_narrowing_constraint(&mut self, predicate: Predicate<'db>) {
self.current_use_def_map_mut().record_constraint(constraint); let use_def = self.current_use_def_map_mut();
let predicate_id = use_def.add_predicate(predicate);
use_def.record_narrowing_constraint(predicate_id);
} }
/// Negates the given constraint and then adds it to all live bindings. /// Negates the given predicate and then adds it as a narrowing constraint to all live
fn record_negated_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId { /// bindings.
let id = self.add_negated_constraint(constraint); fn record_negated_narrowing_constraint(
self.record_constraint_id(id); &mut self,
predicate: Predicate<'db>,
) -> ScopedPredicateId {
let id = self.add_negated_predicate(predicate);
self.record_narrowing_constraint_id(id);
id id
} }
@ -454,12 +464,12 @@ impl<'db> SemanticIndexBuilder<'db> {
/// Records a visibility constraint by applying it to all live bindings and declarations. /// Records a visibility constraint by applying it to all live bindings and declarations.
fn record_visibility_constraint( fn record_visibility_constraint(
&mut self, &mut self,
constraint: Constraint<'db>, predicate: Predicate<'db>,
) -> ScopedVisibilityConstraintId { ) -> ScopedVisibilityConstraintId {
let constraint_id = self.current_use_def_map_mut().add_constraint(constraint); let predicate_id = self.current_use_def_map_mut().add_predicate(predicate);
let id = self let id = self
.current_visibility_constraints_mut() .current_visibility_constraints_mut()
.add_atom(constraint_id); .add_atom(predicate_id);
self.record_visibility_constraint_id(id); self.record_visibility_constraint_id(id);
id id
} }
@ -526,12 +536,12 @@ impl<'db> SemanticIndexBuilder<'db> {
} }
} }
fn add_pattern_constraint( fn add_pattern_narrowing_constraint(
&mut self, &mut self,
subject: Expression<'db>, subject: Expression<'db>,
pattern: &ast::Pattern, pattern: &ast::Pattern,
guard: Option<&ast::Expr>, guard: Option<&ast::Expr>,
) -> Constraint<'db> { ) -> Predicate<'db> {
// This is called for the top-level pattern of each match arm. We need to create a // This is called for the top-level pattern of each match arm. We need to create a
// standalone expression for each arm of a match statement, since they can introduce // standalone expression for each arm of a match statement, since they can introduce
// constraints on the match subject. (Or more accurately, for the match arm's pattern, // constraints on the match subject. (Or more accurately, for the match arm's pattern,
@ -548,19 +558,19 @@ impl<'db> SemanticIndexBuilder<'db> {
let kind = match pattern { let kind = match pattern {
ast::Pattern::MatchValue(pattern) => { ast::Pattern::MatchValue(pattern) => {
let value = self.add_standalone_expression(&pattern.value); let value = self.add_standalone_expression(&pattern.value);
PatternConstraintKind::Value(value, guard) PatternPredicateKind::Value(value, guard)
} }
ast::Pattern::MatchSingleton(singleton) => { ast::Pattern::MatchSingleton(singleton) => {
PatternConstraintKind::Singleton(singleton.value, guard) PatternPredicateKind::Singleton(singleton.value, guard)
} }
ast::Pattern::MatchClass(pattern) => { ast::Pattern::MatchClass(pattern) => {
let cls = self.add_standalone_expression(&pattern.cls); let cls = self.add_standalone_expression(&pattern.cls);
PatternConstraintKind::Class(cls, guard) PatternPredicateKind::Class(cls, guard)
} }
_ => PatternConstraintKind::Unsupported, _ => PatternPredicateKind::Unsupported,
}; };
let pattern_constraint = PatternConstraint::new( let pattern_predicate = PatternPredicate::new(
self.db, self.db,
self.file, self.file,
self.current_scope(), self.current_scope(),
@ -568,12 +578,12 @@ impl<'db> SemanticIndexBuilder<'db> {
kind, kind,
countme::Count::default(), countme::Count::default(),
); );
let constraint = Constraint { let predicate = Predicate {
node: ConstraintNode::Pattern(pattern_constraint), node: PredicateNode::Pattern(pattern_predicate),
is_positive: true, is_positive: true,
}; };
self.current_use_def_map_mut().record_constraint(constraint); self.record_narrowing_constraint(predicate);
constraint predicate
} }
/// Record an expression that needs to be a Salsa ingredient, because we need to infer its type /// Record an expression that needs to be a Salsa ingredient, because we need to infer its type
@ -1116,10 +1126,10 @@ where
ast::Stmt::If(node) => { ast::Stmt::If(node) => {
self.visit_expr(&node.test); self.visit_expr(&node.test);
let mut no_branch_taken = self.flow_snapshot(); let mut no_branch_taken = self.flow_snapshot();
let mut last_constraint = self.record_expression_constraint(&node.test); let mut last_predicate = self.record_expression_narrowing_constraint(&node.test);
self.visit_body(&node.body); self.visit_body(&node.body);
let visibility_constraint_id = self.record_visibility_constraint(last_constraint); let visibility_constraint_id = self.record_visibility_constraint(last_predicate);
let mut vis_constraints = vec![visibility_constraint_id]; let mut vis_constraints = vec![visibility_constraint_id];
let mut post_clauses: Vec<FlowSnapshot> = vec![]; let mut post_clauses: Vec<FlowSnapshot> = vec![];
@ -1145,14 +1155,14 @@ where
// we can only take an elif/else branch if none of the previous ones were // we can only take an elif/else branch if none of the previous ones were
// taken // taken
self.flow_restore(no_branch_taken.clone()); self.flow_restore(no_branch_taken.clone());
self.record_negated_constraint(last_constraint); self.record_negated_narrowing_constraint(last_predicate);
let elif_constraint = if let Some(elif_test) = clause_test { let elif_predicate = if let Some(elif_test) = clause_test {
self.visit_expr(elif_test); self.visit_expr(elif_test);
// A test expression is evaluated whether the branch is taken or not // A test expression is evaluated whether the branch is taken or not
no_branch_taken = self.flow_snapshot(); no_branch_taken = self.flow_snapshot();
let constraint = self.record_expression_constraint(elif_test); let predicate = self.record_expression_narrowing_constraint(elif_test);
Some(constraint) Some(predicate)
} else { } else {
None None
}; };
@ -1162,9 +1172,9 @@ where
for id in &vis_constraints { for id in &vis_constraints {
self.record_negated_visibility_constraint(*id); self.record_negated_visibility_constraint(*id);
} }
if let Some(elif_constraint) = elif_constraint { if let Some(elif_predicate) = elif_predicate {
last_constraint = elif_constraint; last_predicate = elif_predicate;
let id = self.record_visibility_constraint(elif_constraint); let id = self.record_visibility_constraint(elif_predicate);
vis_constraints.push(id); vis_constraints.push(id);
} }
} }
@ -1184,19 +1194,19 @@ where
self.visit_expr(test); self.visit_expr(test);
let pre_loop = self.flow_snapshot(); let pre_loop = self.flow_snapshot();
let constraint = self.record_expression_constraint(test); let predicate = self.record_expression_narrowing_constraint(test);
// We need multiple copies of the visibility constraint for the while condition, // We need multiple copies of the visibility constraint for the while condition,
// since we need to model situations where the first evaluation of the condition // since we need to model situations where the first evaluation of the condition
// returns True, but a later evaluation returns False. // returns True, but a later evaluation returns False.
let first_constraint_id = self.current_use_def_map_mut().add_constraint(constraint); let first_predicate_id = self.current_use_def_map_mut().add_predicate(predicate);
let later_constraint_id = self.current_use_def_map_mut().add_constraint(constraint); let later_predicate_id = self.current_use_def_map_mut().add_predicate(predicate);
let first_vis_constraint_id = self let first_vis_constraint_id = self
.current_visibility_constraints_mut() .current_visibility_constraints_mut()
.add_atom(first_constraint_id); .add_atom(first_predicate_id);
let later_vis_constraint_id = self let later_vis_constraint_id = self
.current_visibility_constraints_mut() .current_visibility_constraints_mut()
.add_atom(later_constraint_id); .add_atom(later_predicate_id);
// Save aside any break states from an outer loop // Save aside any break states from an outer loop
let saved_break_states = std::mem::take(&mut self.loop_break_states); let saved_break_states = std::mem::take(&mut self.loop_break_states);
@ -1234,7 +1244,7 @@ where
self.flow_restore(pre_loop.clone()); self.flow_restore(pre_loop.clone());
self.record_negated_visibility_constraint(first_vis_constraint_id); self.record_negated_visibility_constraint(first_vis_constraint_id);
self.flow_merge(post_body); self.flow_merge(post_body);
self.record_negated_constraint(constraint); self.record_negated_narrowing_constraint(predicate);
self.visit_body(orelse); self.visit_body(orelse);
self.record_negated_visibility_constraint(later_vis_constraint_id); self.record_negated_visibility_constraint(later_vis_constraint_id);
@ -1383,7 +1393,7 @@ where
self.current_match_case = Some(CurrentMatchCase::new(&case.pattern)); self.current_match_case = Some(CurrentMatchCase::new(&case.pattern));
self.visit_pattern(&case.pattern); self.visit_pattern(&case.pattern);
self.current_match_case = None; self.current_match_case = None;
let constraint_id = self.add_pattern_constraint( let predicate = self.add_pattern_narrowing_constraint(
subject_expr, subject_expr,
&case.pattern, &case.pattern,
case.guard.as_deref(), case.guard.as_deref(),
@ -1395,7 +1405,7 @@ where
for id in &vis_constraints { for id in &vis_constraints {
self.record_negated_visibility_constraint(*id); self.record_negated_visibility_constraint(*id);
} }
let vis_constraint_id = self.record_visibility_constraint(constraint_id); let vis_constraint_id = self.record_visibility_constraint(predicate);
vis_constraints.push(vis_constraint_id); vis_constraints.push(vis_constraint_id);
} }
@ -1694,13 +1704,13 @@ where
}) => { }) => {
self.visit_expr(test); self.visit_expr(test);
let pre_if = self.flow_snapshot(); let pre_if = self.flow_snapshot();
let constraint = self.record_expression_constraint(test); let predicate = self.record_expression_narrowing_constraint(test);
self.visit_expr(body); self.visit_expr(body);
let visibility_constraint = self.record_visibility_constraint(constraint); let visibility_constraint = self.record_visibility_constraint(predicate);
let post_body = self.flow_snapshot(); let post_body = self.flow_snapshot();
self.flow_restore(pre_if.clone()); self.flow_restore(pre_if.clone());
self.record_negated_constraint(constraint); self.record_negated_narrowing_constraint(predicate);
self.visit_expr(orelse); self.visit_expr(orelse);
self.record_negated_visibility_constraint(visibility_constraint); self.record_negated_visibility_constraint(visibility_constraint);
self.flow_merge(post_body); self.flow_merge(post_body);
@ -1776,14 +1786,14 @@ where
// For the last value, we don't need to model control flow. There is short-circuiting // For the last value, we don't need to model control flow. There is short-circuiting
// anymore. // anymore.
if index < values.len() - 1 { if index < values.len() - 1 {
let constraint = self.build_constraint(value); let predicate = self.build_predicate(value);
let constraint_id = match op { let predicate_id = match op {
ast::BoolOp::And => self.add_constraint(constraint), ast::BoolOp::And => self.add_predicate(predicate),
ast::BoolOp::Or => self.add_negated_constraint(constraint), ast::BoolOp::Or => self.add_negated_predicate(predicate),
}; };
let visibility_constraint = self let visibility_constraint = self
.current_visibility_constraints_mut() .current_visibility_constraints_mut()
.add_atom(constraint_id); .add_atom(predicate_id);
let after_expr = self.flow_snapshot(); let after_expr = self.flow_snapshot();
@ -1801,7 +1811,7 @@ where
// the application of the visibility constraint until after the expression // the application of the visibility constraint until after the expression
// has been evaluated, so we only push it onto the stack here. // has been evaluated, so we only push it onto the stack here.
self.flow_restore(after_expr); self.flow_restore(after_expr);
self.record_constraint_id(constraint_id); self.record_narrowing_constraint_id(predicate_id);
visibility_constraints.push(visibility_constraint); visibility_constraints.push(visibility_constraint);
} }
} }

View file

@ -1,77 +0,0 @@
use ruff_db::files::File;
use ruff_index::{newtype_index, IndexVec};
use ruff_python_ast::Singleton;
use crate::db::Db;
use crate::semantic_index::expression::Expression;
use crate::semantic_index::symbol::{FileScopeId, ScopeId};
// A scoped identifier for each `Constraint` in a scope.
#[newtype_index]
#[derive(Ord, PartialOrd)]
pub(crate) struct ScopedConstraintId;
// A collection of constraints. This is currently stored in `UseDefMap`, which means we maintain a
// separate set of constraints for each scope in a file.
pub(crate) type Constraints<'db> = IndexVec<ScopedConstraintId, Constraint<'db>>;
#[derive(Debug, Default)]
pub(crate) struct ConstraintsBuilder<'db> {
constraints: IndexVec<ScopedConstraintId, Constraint<'db>>,
}
impl<'db> ConstraintsBuilder<'db> {
/// Adds a constraint. Note that we do not deduplicate constraints. If you add a `Constraint`
/// more than once, you will get distinct `ScopedConstraintId`s for each one. (This lets you
/// model constraint expressions that might evaluate to different values at different points of
/// execution.)
pub(crate) fn add_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId {
self.constraints.push(constraint)
}
pub(crate) fn build(mut self) -> Constraints<'db> {
self.constraints.shrink_to_fit();
self.constraints
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
pub(crate) struct Constraint<'db> {
pub(crate) node: ConstraintNode<'db>,
pub(crate) is_positive: bool,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
pub(crate) enum ConstraintNode<'db> {
Expression(Expression<'db>),
Pattern(PatternConstraint<'db>),
}
/// Pattern kinds for which we support type narrowing and/or static visibility analysis.
#[derive(Debug, Clone, Hash, PartialEq, salsa::Update)]
pub(crate) enum PatternConstraintKind<'db> {
Singleton(Singleton, Option<Expression<'db>>),
Value(Expression<'db>, Option<Expression<'db>>),
Class(Expression<'db>, Option<Expression<'db>>),
Unsupported,
}
#[salsa::tracked]
pub(crate) struct PatternConstraint<'db> {
pub(crate) file: File,
pub(crate) file_scope: FileScopeId,
pub(crate) subject: Expression<'db>,
#[return_ref]
pub(crate) kind: PatternConstraintKind<'db>,
count: countme::Count<PatternConstraint<'static>>,
}
impl<'db> PatternConstraint<'db> {
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}
}

View file

@ -1,77 +1,77 @@
//! # Narrowing constraints //! # Narrowing constraints
//! //!
//! When building a semantic index for a file, we associate each binding with _narrowing //! When building a semantic index for a file, we associate each binding with a _narrowing
//! constraints_. The narrowing constraint is used to constrain the type of the binding's symbol. //! constraint_, which constrains the type of the binding's symbol. Note that a binding can be
//! Note that a binding can be associated with a different narrowing constraint at different points //! associated with a different narrowing constraint at different points in a file. See the
//! in a file. See the [`use_def`][crate::semantic_index::use_def] module for more details. //! [`use_def`][crate::semantic_index::use_def] module for more details.
//! //!
//! This module defines how narrowing constraints are stored internally. //! This module defines how narrowing constraints are stored internally.
//! //!
//! A _narrowing constraint_ consists of a list of _clauses_, each of which corresponds with an //! A _narrowing constraint_ consists of a list of _predicates_, each of which corresponds with an
//! expression in the source file (represented by a [`Constraint`]). We need to support the //! expression in the source file (represented by a [`Predicate`]). We need to support the
//! following operations on narrowing constraints: //! following operations on narrowing constraints:
//! //!
//! - Adding a new clause to an existing constraint //! - Adding a new predicate to an existing constraint
//! - Merging two constraints together, which produces the _intersection_ of their clauses //! - Merging two constraints together, which produces the _intersection_ of their predicates
//! - Iterating through the clauses in a constraint //! - Iterating through the predicates in a constraint
//! //!
//! In particular, note that we do not need random access to the clauses in a constraint. That //! In particular, note that we do not need random access to the predicates in a constraint. That
//! means that we can use a simple [_sorted association list_][ruff_index::list] as our data //! means that we can use a simple [_sorted association list_][ruff_index::list] as our data
//! structure. That lets us use a single 32-bit integer to store each narrowing constraint, no //! structure. That lets us use a single 32-bit integer to store each narrowing constraint, no
//! matter how many clauses it contains. It also makes merging two narrowing constraints fast, //! matter how many predicates it contains. It also makes merging two narrowing constraints fast,
//! since alists support fast intersection. //! since alists support fast intersection.
//! //!
//! Because we visit the contents of each scope in source-file order, and assign scoped IDs in //! Because we visit the contents of each scope in source-file order, and assign scoped IDs in
//! source-file order, that means that we will tend to visit narrowing constraints in order by //! source-file order, that means that we will tend to visit narrowing constraints in order by
//! their IDs. This is exactly how to get the best performance from our alist implementation. //! their predicate IDs. This is exactly how to get the best performance from our alist
//! implementation.
//! //!
//! [`Constraint`]: crate::semantic_index::constraint::Constraint //! [`Predicate`]: crate::semantic_index::predicate::Predicate
use ruff_index::list::{ListBuilder, ListSetReverseIterator, ListStorage}; use ruff_index::list::{ListBuilder, ListSetReverseIterator, ListStorage};
use ruff_index::newtype_index; use ruff_index::newtype_index;
use crate::semantic_index::constraint::ScopedConstraintId; use crate::semantic_index::predicate::ScopedPredicateId;
/// A narrowing constraint associated with a live binding. /// A narrowing constraint associated with a live binding.
/// ///
/// A constraint is a list of clauses, each of which is a [`Constraint`] that constrains the type /// A constraint is a list of [`Predicate`]s that each constrain the type of the binding's symbol.
/// of the binding's symbol.
/// ///
/// An instance of this type represents a _non-empty_ narrowing constraint. You will often wrap /// An instance of this type represents a _non-empty_ narrowing constraint. You will often wrap
/// this in `Option` and use `None` to represent an empty narrowing constraint. /// this in `Option` and use `None` to represent an empty narrowing constraint.
/// ///
/// [`Constraint`]: crate::semantic_index::constraint::Constraint /// [`Predicate`]: crate::semantic_index::predicate::Predicate
#[newtype_index] #[newtype_index]
pub(crate) struct ScopedNarrowingConstraintId; pub(crate) struct ScopedNarrowingConstraintId;
/// One of the clauses in a narrowing constraint, which is a [`Constraint`] that constrains the /// One of the [`Predicate`]s in a narrowing constraint, which constraints the type of the
/// type of the binding's symbol. /// binding's symbol.
/// ///
/// Note that those [`Constraint`]s are stored in [their own per-scope /// Note that those [`Predicate`]s are stored in [their own per-scope
/// arena][crate::semantic_index::constraint::Constraints], so internally we use a /// arena][crate::semantic_index::predicate::Predicates], so internally we use a
/// [`ScopedConstraintId`] to refer to the underlying constraint. /// [`ScopedPredicateId`] to refer to the underlying predicate.
/// ///
/// [`Constraint`]: crate::semantic_index::constraint::Constraint /// [`Predicate`]: crate::semantic_index::predicate::Predicate
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) struct ScopedNarrowingConstraintClause(ScopedConstraintId); pub(crate) struct ScopedNarrowingConstraintPredicate(ScopedPredicateId);
impl ScopedNarrowingConstraintClause { impl ScopedNarrowingConstraintPredicate {
/// Returns (the ID of) the `Constraint` for this clause /// Returns (the ID of) the `Predicate`
pub(crate) fn constraint(self) -> ScopedConstraintId { pub(crate) fn predicate(self) -> ScopedPredicateId {
self.0 self.0
} }
} }
impl From<ScopedConstraintId> for ScopedNarrowingConstraintClause { impl From<ScopedPredicateId> for ScopedNarrowingConstraintPredicate {
fn from(constraint: ScopedConstraintId) -> ScopedNarrowingConstraintClause { fn from(predicate: ScopedPredicateId) -> ScopedNarrowingConstraintPredicate {
ScopedNarrowingConstraintClause(constraint) ScopedNarrowingConstraintPredicate(predicate)
} }
} }
/// A collection of narrowing constraints for a given scope. /// A collection of narrowing constraints for a given scope.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub(crate) struct NarrowingConstraints { pub(crate) struct NarrowingConstraints {
lists: ListStorage<ScopedNarrowingConstraintId, ScopedNarrowingConstraintClause>, lists: ListStorage<ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate>,
} }
// Building constraints // Building constraints
@ -80,7 +80,7 @@ pub(crate) struct NarrowingConstraints {
/// A builder for creating narrowing constraints. /// A builder for creating narrowing constraints.
#[derive(Debug, Default, Eq, PartialEq)] #[derive(Debug, Default, Eq, PartialEq)]
pub(crate) struct NarrowingConstraintsBuilder { pub(crate) struct NarrowingConstraintsBuilder {
lists: ListBuilder<ScopedNarrowingConstraintId, ScopedNarrowingConstraintClause>, lists: ListBuilder<ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate>,
} }
impl NarrowingConstraintsBuilder { impl NarrowingConstraintsBuilder {
@ -90,18 +90,18 @@ impl NarrowingConstraintsBuilder {
} }
} }
/// Adds a clause to an existing narrowing constraint. /// Adds a predicate to an existing narrowing constraint.
pub(crate) fn add( pub(crate) fn add_predicate_to_constraint(
&mut self, &mut self,
constraint: Option<ScopedNarrowingConstraintId>, constraint: Option<ScopedNarrowingConstraintId>,
clause: ScopedNarrowingConstraintClause, predicate: ScopedNarrowingConstraintPredicate,
) -> Option<ScopedNarrowingConstraintId> { ) -> Option<ScopedNarrowingConstraintId> {
self.lists.insert(constraint, clause) self.lists.insert(constraint, predicate)
} }
/// Returns the intersection of two narrowing constraints. The result contains the clauses that /// Returns the intersection of two narrowing constraints. The result contains the predicates
/// appear in both inputs. /// that appear in both inputs.
pub(crate) fn intersect( pub(crate) fn intersect_constraints(
&mut self, &mut self,
a: Option<ScopedNarrowingConstraintId>, a: Option<ScopedNarrowingConstraintId>,
b: Option<ScopedNarrowingConstraintId>, b: Option<ScopedNarrowingConstraintId>,
@ -114,12 +114,12 @@ impl NarrowingConstraintsBuilder {
// --------- // ---------
pub(crate) type NarrowingConstraintsIterator<'a> = std::iter::Copied< pub(crate) type NarrowingConstraintsIterator<'a> = std::iter::Copied<
ListSetReverseIterator<'a, ScopedNarrowingConstraintId, ScopedNarrowingConstraintClause>, ListSetReverseIterator<'a, ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate>,
>; >;
impl NarrowingConstraints { impl NarrowingConstraints {
/// Iterates over the clauses in a narrowing constraint. /// Iterates over the predicates in a narrowing constraint.
pub(crate) fn iter_clauses( pub(crate) fn iter_predicates(
&self, &self,
set: Option<ScopedNarrowingConstraintId>, set: Option<ScopedNarrowingConstraintId>,
) -> NarrowingConstraintsIterator<'_> { ) -> NarrowingConstraintsIterator<'_> {
@ -134,14 +134,14 @@ impl NarrowingConstraints {
mod tests { mod tests {
use super::*; use super::*;
impl ScopedNarrowingConstraintClause { impl ScopedNarrowingConstraintPredicate {
pub(crate) fn as_u32(self) -> u32 { pub(crate) fn as_u32(self) -> u32 {
self.0.as_u32() self.0.as_u32()
} }
} }
impl NarrowingConstraintsBuilder { impl NarrowingConstraintsBuilder {
pub(crate) fn iter_constraints( pub(crate) fn iter_predicates(
&self, &self,
set: Option<ScopedNarrowingConstraintId>, set: Option<ScopedNarrowingConstraintId>,
) -> NarrowingConstraintsIterator<'_> { ) -> NarrowingConstraintsIterator<'_> {

View file

@ -0,0 +1,84 @@
//! _Predicates_ are Python expressions whose runtime values can affect type inference.
//!
//! We currently use predicates in two places:
//!
//! - [_Narrowing constraints_][crate::semantic_index::narrowing_constraints] constrain the type of
//! a binding that is visible at a particular use.
//! - [_Visibility constraints_][crate::semantic_index::visibility_constraints] determine the
//! static visibility of a binding, and the reachability of a statement.
use ruff_db::files::File;
use ruff_index::{newtype_index, IndexVec};
use ruff_python_ast::Singleton;
use crate::db::Db;
use crate::semantic_index::expression::Expression;
use crate::semantic_index::symbol::{FileScopeId, ScopeId};
// A scoped identifier for each `Predicate` in a scope.
#[newtype_index]
#[derive(Ord, PartialOrd)]
pub(crate) struct ScopedPredicateId;
// A collection of predicates for a given scope.
pub(crate) type Predicates<'db> = IndexVec<ScopedPredicateId, Predicate<'db>>;
#[derive(Debug, Default)]
pub(crate) struct PredicatesBuilder<'db> {
predicates: IndexVec<ScopedPredicateId, Predicate<'db>>,
}
impl<'db> PredicatesBuilder<'db> {
/// Adds a predicate. Note that we do not deduplicate predicates. If you add a `Predicate`
/// more than once, you will get distinct `ScopedPredicateId`s for each one. (This lets you
/// model predicates that might evaluate to different values at different points of execution.)
pub(crate) fn add_predicate(&mut self, predicate: Predicate<'db>) -> ScopedPredicateId {
self.predicates.push(predicate)
}
pub(crate) fn build(mut self) -> Predicates<'db> {
self.predicates.shrink_to_fit();
self.predicates
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
pub(crate) struct Predicate<'db> {
pub(crate) node: PredicateNode<'db>,
pub(crate) is_positive: bool,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
pub(crate) enum PredicateNode<'db> {
Expression(Expression<'db>),
Pattern(PatternPredicate<'db>),
}
/// Pattern kinds for which we support type narrowing and/or static visibility analysis.
#[derive(Debug, Clone, Hash, PartialEq, salsa::Update)]
pub(crate) enum PatternPredicateKind<'db> {
Singleton(Singleton, Option<Expression<'db>>),
Value(Expression<'db>, Option<Expression<'db>>),
Class(Expression<'db>, Option<Expression<'db>>),
Unsupported,
}
#[salsa::tracked]
pub(crate) struct PatternPredicate<'db> {
pub(crate) file: File,
pub(crate) file_scope: FileScopeId,
pub(crate) subject: Expression<'db>,
#[return_ref]
pub(crate) kind: PatternPredicateKind<'db>,
count: countme::Count<PatternPredicate<'static>>,
}
impl<'db> PatternPredicate<'db> {
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}
}

View file

@ -165,7 +165,7 @@
//! don't actually store these "list of visible definitions" as a vector of [`Definition`]. //! don't actually store these "list of visible definitions" as a vector of [`Definition`].
//! Instead, [`SymbolBindings`] and [`SymbolDeclarations`] are structs which use bit-sets to track //! Instead, [`SymbolBindings`] and [`SymbolDeclarations`] are structs which use bit-sets to track
//! definitions (and constraints, in the case of bindings) in terms of [`ScopedDefinitionId`] and //! definitions (and constraints, in the case of bindings) in terms of [`ScopedDefinitionId`] and
//! [`ScopedConstraintId`], which are indices into the `all_definitions` and `constraints` //! [`ScopedPredicateId`], which are indices into the `all_definitions` and `predicates`
//! indexvecs in the [`UseDefMap`]. //! indexvecs in the [`UseDefMap`].
//! //!
//! There is another special kind of possible "definition" for a symbol: there might be a path from //! There is another special kind of possible "definition" for a symbol: there might be a path from
@ -264,13 +264,13 @@ use self::symbol_state::{
SymbolBindings, SymbolDeclarations, SymbolState, SymbolBindings, SymbolDeclarations, SymbolState,
}; };
use crate::semantic_index::ast_ids::ScopedUseId; use crate::semantic_index::ast_ids::ScopedUseId;
use crate::semantic_index::constraint::{
Constraint, Constraints, ConstraintsBuilder, ScopedConstraintId,
};
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::semantic_index::narrowing_constraints::{ use crate::semantic_index::narrowing_constraints::{
NarrowingConstraints, NarrowingConstraintsBuilder, NarrowingConstraintsIterator, NarrowingConstraints, NarrowingConstraintsBuilder, NarrowingConstraintsIterator,
}; };
use crate::semantic_index::predicate::{
Predicate, Predicates, PredicatesBuilder, ScopedPredicateId,
};
use crate::semantic_index::symbol::{FileScopeId, ScopedSymbolId}; use crate::semantic_index::symbol::{FileScopeId, ScopedSymbolId};
use crate::semantic_index::visibility_constraints::{ use crate::semantic_index::visibility_constraints::{
ScopedVisibilityConstraintId, VisibilityConstraints, VisibilityConstraintsBuilder, ScopedVisibilityConstraintId, VisibilityConstraints, VisibilityConstraintsBuilder,
@ -285,8 +285,8 @@ pub(crate) struct UseDefMap<'db> {
/// this represents the implicit "unbound"/"undeclared" definition of every symbol. /// this represents the implicit "unbound"/"undeclared" definition of every symbol.
all_definitions: IndexVec<ScopedDefinitionId, Option<Definition<'db>>>, all_definitions: IndexVec<ScopedDefinitionId, Option<Definition<'db>>>,
/// Array of [`Constraint`] in this scope. /// Array of predicates in this scope.
constraints: Constraints<'db>, predicates: Predicates<'db>,
/// Array of narrowing constraints in this scope. /// Array of narrowing constraints in this scope.
narrowing_constraints: NarrowingConstraints, narrowing_constraints: NarrowingConstraints,
@ -374,7 +374,7 @@ impl<'db> UseDefMap<'db> {
) -> BindingWithConstraintsIterator<'map, 'db> { ) -> BindingWithConstraintsIterator<'map, 'db> {
BindingWithConstraintsIterator { BindingWithConstraintsIterator {
all_definitions: &self.all_definitions, all_definitions: &self.all_definitions,
constraints: &self.constraints, predicates: &self.predicates,
narrowing_constraints: &self.narrowing_constraints, narrowing_constraints: &self.narrowing_constraints,
visibility_constraints: &self.visibility_constraints, visibility_constraints: &self.visibility_constraints,
inner: bindings.iter(), inner: bindings.iter(),
@ -387,7 +387,7 @@ impl<'db> UseDefMap<'db> {
) -> DeclarationsIterator<'map, 'db> { ) -> DeclarationsIterator<'map, 'db> {
DeclarationsIterator { DeclarationsIterator {
all_definitions: &self.all_definitions, all_definitions: &self.all_definitions,
constraints: &self.constraints, predicates: &self.predicates,
visibility_constraints: &self.visibility_constraints, visibility_constraints: &self.visibility_constraints,
inner: declarations.iter(), inner: declarations.iter(),
} }
@ -421,7 +421,7 @@ type EagerBindings = IndexVec<ScopedEagerBindingsId, SymbolBindings>;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct BindingWithConstraintsIterator<'map, 'db> { pub(crate) struct BindingWithConstraintsIterator<'map, 'db> {
all_definitions: &'map IndexVec<ScopedDefinitionId, Option<Definition<'db>>>, all_definitions: &'map IndexVec<ScopedDefinitionId, Option<Definition<'db>>>,
pub(crate) constraints: &'map Constraints<'db>, pub(crate) predicates: &'map Predicates<'db>,
pub(crate) narrowing_constraints: &'map NarrowingConstraints, pub(crate) narrowing_constraints: &'map NarrowingConstraints,
pub(crate) visibility_constraints: &'map VisibilityConstraints, pub(crate) visibility_constraints: &'map VisibilityConstraints,
inner: LiveBindingsIterator<'map>, inner: LiveBindingsIterator<'map>,
@ -431,7 +431,7 @@ impl<'map, 'db> Iterator for BindingWithConstraintsIterator<'map, 'db> {
type Item = BindingWithConstraints<'map, 'db>; type Item = BindingWithConstraints<'map, 'db>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let constraints = self.constraints; let predicates = self.predicates;
let narrowing_constraints = self.narrowing_constraints; let narrowing_constraints = self.narrowing_constraints;
self.inner self.inner
@ -439,9 +439,9 @@ impl<'map, 'db> Iterator for BindingWithConstraintsIterator<'map, 'db> {
.map(|live_binding| BindingWithConstraints { .map(|live_binding| BindingWithConstraints {
binding: self.all_definitions[live_binding.binding], binding: self.all_definitions[live_binding.binding],
narrowing_constraint: ConstraintsIterator { narrowing_constraint: ConstraintsIterator {
constraints, predicates,
constraint_ids: narrowing_constraints constraint_ids: narrowing_constraints
.iter_clauses(live_binding.narrowing_constraint), .iter_predicates(live_binding.narrowing_constraint),
}, },
visibility_constraint: live_binding.visibility_constraint, visibility_constraint: live_binding.visibility_constraint,
}) })
@ -457,17 +457,17 @@ pub(crate) struct BindingWithConstraints<'map, 'db> {
} }
pub(crate) struct ConstraintsIterator<'map, 'db> { pub(crate) struct ConstraintsIterator<'map, 'db> {
constraints: &'map Constraints<'db>, predicates: &'map Predicates<'db>,
constraint_ids: NarrowingConstraintsIterator<'map>, constraint_ids: NarrowingConstraintsIterator<'map>,
} }
impl<'db> Iterator for ConstraintsIterator<'_, 'db> { impl<'db> Iterator for ConstraintsIterator<'_, 'db> {
type Item = Constraint<'db>; type Item = Predicate<'db>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.constraint_ids self.constraint_ids
.next() .next()
.map(|narrowing_constraint| self.constraints[narrowing_constraint.constraint()]) .map(|narrowing_constraint| self.predicates[narrowing_constraint.predicate()])
} }
} }
@ -475,7 +475,7 @@ impl std::iter::FusedIterator for ConstraintsIterator<'_, '_> {}
pub(crate) struct DeclarationsIterator<'map, 'db> { pub(crate) struct DeclarationsIterator<'map, 'db> {
all_definitions: &'map IndexVec<ScopedDefinitionId, Option<Definition<'db>>>, all_definitions: &'map IndexVec<ScopedDefinitionId, Option<Definition<'db>>>,
pub(crate) constraints: &'map Constraints<'db>, pub(crate) predicates: &'map Predicates<'db>,
pub(crate) visibility_constraints: &'map VisibilityConstraints, pub(crate) visibility_constraints: &'map VisibilityConstraints,
inner: LiveDeclarationsIterator<'map>, inner: LiveDeclarationsIterator<'map>,
} }
@ -517,8 +517,8 @@ pub(super) struct UseDefMapBuilder<'db> {
/// Append-only array of [`Definition`]. /// Append-only array of [`Definition`].
all_definitions: IndexVec<ScopedDefinitionId, Option<Definition<'db>>>, all_definitions: IndexVec<ScopedDefinitionId, Option<Definition<'db>>>,
/// Builder of constraints. /// Builder of predicates.
pub(super) constraints: ConstraintsBuilder<'db>, pub(super) predicates: PredicatesBuilder<'db>,
/// Builder of narrowing constraints. /// Builder of narrowing constraints.
pub(super) narrowing_constraints: NarrowingConstraintsBuilder, pub(super) narrowing_constraints: NarrowingConstraintsBuilder,
@ -553,7 +553,7 @@ impl Default for UseDefMapBuilder<'_> {
fn default() -> Self { fn default() -> Self {
Self { Self {
all_definitions: IndexVec::from_iter([None]), all_definitions: IndexVec::from_iter([None]),
constraints: ConstraintsBuilder::default(), predicates: PredicatesBuilder::default(),
narrowing_constraints: NarrowingConstraintsBuilder::default(), narrowing_constraints: NarrowingConstraintsBuilder::default(),
visibility_constraints: VisibilityConstraintsBuilder::default(), visibility_constraints: VisibilityConstraintsBuilder::default(),
scope_start_visibility: ScopedVisibilityConstraintId::ALWAYS_TRUE, scope_start_visibility: ScopedVisibilityConstraintId::ALWAYS_TRUE,
@ -586,23 +586,18 @@ impl<'db> UseDefMapBuilder<'db> {
symbol_state.record_binding(def_id, self.scope_start_visibility); symbol_state.record_binding(def_id, self.scope_start_visibility);
} }
pub(super) fn add_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId { pub(super) fn add_predicate(&mut self, predicate: Predicate<'db>) -> ScopedPredicateId {
self.constraints.add_constraint(constraint) self.predicates.add_predicate(predicate)
} }
pub(super) fn record_constraint_id(&mut self, constraint: ScopedConstraintId) { pub(super) fn record_narrowing_constraint(&mut self, predicate: ScopedPredicateId) {
let narrowing_constraint = constraint.into(); let narrowing_constraint = predicate.into();
for state in &mut self.symbol_states { for state in &mut self.symbol_states {
state.record_constraint(&mut self.narrowing_constraints, narrowing_constraint); state
.record_narrowing_constraint(&mut self.narrowing_constraints, narrowing_constraint);
} }
} }
pub(super) fn record_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId {
let new_constraint_id = self.add_constraint(constraint);
self.record_constraint_id(new_constraint_id);
new_constraint_id
}
pub(super) fn record_visibility_constraint( pub(super) fn record_visibility_constraint(
&mut self, &mut self,
constraint: ScopedVisibilityConstraintId, constraint: ScopedVisibilityConstraintId,
@ -781,7 +776,7 @@ impl<'db> UseDefMapBuilder<'db> {
UseDefMap { UseDefMap {
all_definitions: self.all_definitions, all_definitions: self.all_definitions,
constraints: self.constraints.build(), predicates: self.predicates.build(),
narrowing_constraints: self.narrowing_constraints.build(), narrowing_constraints: self.narrowing_constraints.build(),
visibility_constraints: self.visibility_constraints.build(), visibility_constraints: self.visibility_constraints.build(),
bindings_by_use: self.bindings_by_use, bindings_by_use: self.bindings_by_use,

View file

@ -47,7 +47,7 @@ use ruff_index::newtype_index;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::semantic_index::narrowing_constraints::{ use crate::semantic_index::narrowing_constraints::{
NarrowingConstraintsBuilder, ScopedNarrowingConstraintClause, ScopedNarrowingConstraintId, NarrowingConstraintsBuilder, ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate,
}; };
use crate::semantic_index::visibility_constraints::{ use crate::semantic_index::visibility_constraints::{
ScopedVisibilityConstraintId, VisibilityConstraintsBuilder, ScopedVisibilityConstraintId, VisibilityConstraintsBuilder,
@ -224,14 +224,14 @@ impl SymbolBindings {
} }
/// Add given constraint to all live bindings. /// Add given constraint to all live bindings.
pub(super) fn record_constraint( pub(super) fn record_narrowing_constraint(
&mut self, &mut self,
narrowing_constraints: &mut NarrowingConstraintsBuilder, narrowing_constraints: &mut NarrowingConstraintsBuilder,
constraint: ScopedNarrowingConstraintClause, predicate: ScopedNarrowingConstraintPredicate,
) { ) {
for binding in &mut self.live_bindings { for binding in &mut self.live_bindings {
binding.narrowing_constraint = binding.narrowing_constraint = narrowing_constraints
narrowing_constraints.add(binding.narrowing_constraint, constraint); .add_predicate_to_constraint(binding.narrowing_constraint, predicate);
} }
} }
@ -292,7 +292,7 @@ impl SymbolBindings {
// that applies on only one path is irrelevant to the resulting type from // that applies on only one path is irrelevant to the resulting type from
// unioning the two paths, so we intersect the constraints. // unioning the two paths, so we intersect the constraints.
let narrowing_constraint = narrowing_constraints let narrowing_constraint = narrowing_constraints
.intersect(a.narrowing_constraint, b.narrowing_constraint); .intersect_constraints(a.narrowing_constraint, b.narrowing_constraint);
// For visibility constraints, we merge them using a ternary OR operation: // For visibility constraints, we merge them using a ternary OR operation:
let visibility_constraint = visibility_constraints let visibility_constraint = visibility_constraints
@ -340,13 +340,13 @@ impl SymbolState {
} }
/// Add given constraint to all live bindings. /// Add given constraint to all live bindings.
pub(super) fn record_constraint( pub(super) fn record_narrowing_constraint(
&mut self, &mut self,
narrowing_constraints: &mut NarrowingConstraintsBuilder, narrowing_constraints: &mut NarrowingConstraintsBuilder,
constraint: ScopedNarrowingConstraintClause, constraint: ScopedNarrowingConstraintPredicate,
) { ) {
self.bindings self.bindings
.record_constraint(narrowing_constraints, constraint); .record_narrowing_constraint(narrowing_constraints, constraint);
} }
/// Add given visibility constraint to all live bindings. /// Add given visibility constraint to all live bindings.
@ -402,7 +402,7 @@ impl SymbolState {
mod tests { mod tests {
use super::*; use super::*;
use crate::semantic_index::constraint::ScopedConstraintId; use crate::semantic_index::predicate::ScopedPredicateId;
#[track_caller] #[track_caller]
fn assert_bindings( fn assert_bindings(
@ -420,12 +420,12 @@ mod tests {
} else { } else {
def_id.as_u32().to_string() def_id.as_u32().to_string()
}; };
let constraints = narrowing_constraints let predicates = narrowing_constraints
.iter_constraints(live_binding.narrowing_constraint) .iter_predicates(live_binding.narrowing_constraint)
.map(|idx| idx.as_u32().to_string()) .map(|idx| idx.as_u32().to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
format!("{def}<{constraints}>") format!("{def}<{predicates}>")
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(actual, expected); assert_eq!(actual, expected);
@ -480,8 +480,8 @@ mod tests {
ScopedDefinitionId::from_u32(1), ScopedDefinitionId::from_u32(1),
ScopedVisibilityConstraintId::ALWAYS_TRUE, ScopedVisibilityConstraintId::ALWAYS_TRUE,
); );
let constraint = ScopedConstraintId::from_u32(0).into(); let predicate = ScopedPredicateId::from_u32(0).into();
sym.record_constraint(&mut narrowing_constraints, constraint); sym.record_narrowing_constraint(&mut narrowing_constraints, predicate);
assert_bindings(&narrowing_constraints, &sym, &["1<0>"]); assert_bindings(&narrowing_constraints, &sym, &["1<0>"]);
} }
@ -497,16 +497,16 @@ mod tests {
ScopedDefinitionId::from_u32(1), ScopedDefinitionId::from_u32(1),
ScopedVisibilityConstraintId::ALWAYS_TRUE, ScopedVisibilityConstraintId::ALWAYS_TRUE,
); );
let constraint = ScopedConstraintId::from_u32(0).into(); let predicate = ScopedPredicateId::from_u32(0).into();
sym1a.record_constraint(&mut narrowing_constraints, constraint); sym1a.record_narrowing_constraint(&mut narrowing_constraints, predicate);
let mut sym1b = SymbolState::undefined(ScopedVisibilityConstraintId::ALWAYS_TRUE); let mut sym1b = SymbolState::undefined(ScopedVisibilityConstraintId::ALWAYS_TRUE);
sym1b.record_binding( sym1b.record_binding(
ScopedDefinitionId::from_u32(1), ScopedDefinitionId::from_u32(1),
ScopedVisibilityConstraintId::ALWAYS_TRUE, ScopedVisibilityConstraintId::ALWAYS_TRUE,
); );
let constraint = ScopedConstraintId::from_u32(0).into(); let predicate = ScopedPredicateId::from_u32(0).into();
sym1b.record_constraint(&mut narrowing_constraints, constraint); sym1b.record_narrowing_constraint(&mut narrowing_constraints, predicate);
sym1a.merge( sym1a.merge(
sym1b, sym1b,
@ -522,16 +522,16 @@ mod tests {
ScopedDefinitionId::from_u32(2), ScopedDefinitionId::from_u32(2),
ScopedVisibilityConstraintId::ALWAYS_TRUE, ScopedVisibilityConstraintId::ALWAYS_TRUE,
); );
let constraint = ScopedConstraintId::from_u32(1).into(); let predicate = ScopedPredicateId::from_u32(1).into();
sym2a.record_constraint(&mut narrowing_constraints, constraint); sym2a.record_narrowing_constraint(&mut narrowing_constraints, predicate);
let mut sym1b = SymbolState::undefined(ScopedVisibilityConstraintId::ALWAYS_TRUE); let mut sym1b = SymbolState::undefined(ScopedVisibilityConstraintId::ALWAYS_TRUE);
sym1b.record_binding( sym1b.record_binding(
ScopedDefinitionId::from_u32(2), ScopedDefinitionId::from_u32(2),
ScopedVisibilityConstraintId::ALWAYS_TRUE, ScopedVisibilityConstraintId::ALWAYS_TRUE,
); );
let constraint = ScopedConstraintId::from_u32(2).into(); let predicate = ScopedPredicateId::from_u32(2).into();
sym1b.record_constraint(&mut narrowing_constraints, constraint); sym1b.record_narrowing_constraint(&mut narrowing_constraints, predicate);
sym2a.merge( sym2a.merge(
sym1b, sym1b,
@ -547,8 +547,8 @@ mod tests {
ScopedDefinitionId::from_u32(3), ScopedDefinitionId::from_u32(3),
ScopedVisibilityConstraintId::ALWAYS_TRUE, ScopedVisibilityConstraintId::ALWAYS_TRUE,
); );
let constraint = ScopedConstraintId::from_u32(3).into(); let predicate = ScopedPredicateId::from_u32(3).into();
sym3a.record_constraint(&mut narrowing_constraints, constraint); sym3a.record_narrowing_constraint(&mut narrowing_constraints, predicate);
let sym2b = SymbolState::undefined(ScopedVisibilityConstraintId::ALWAYS_TRUE); let sym2b = SymbolState::undefined(ScopedVisibilityConstraintId::ALWAYS_TRUE);

View file

@ -178,8 +178,8 @@ use std::cmp::Ordering;
use ruff_index::{Idx, IndexVec}; use ruff_index::{Idx, IndexVec};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::semantic_index::constraint::{ use crate::semantic_index::predicate::{
Constraint, ConstraintNode, Constraints, PatternConstraintKind, ScopedConstraintId, PatternPredicateKind, Predicate, PredicateNode, Predicates, ScopedPredicateId,
}; };
use crate::types::{infer_expression_type, Truthiness}; use crate::types::{infer_expression_type, Truthiness};
use crate::Db; use crate::Db;
@ -188,7 +188,7 @@ use crate::Db;
/// is just like a boolean formula, but with `Ambiguous` as a third potential result. See the /// is just like a boolean formula, but with `Ambiguous` as a third potential result. See the
/// module documentation for more details.) /// module documentation for more details.)
/// ///
/// The primitive atoms of the formula are [`Constraint`]s, which express some property of the /// The primitive atoms of the formula are [`Predicate`]s, which express some property of the
/// runtime state of the code that we are analyzing. /// runtime state of the code that we are analyzing.
/// ///
/// We assume that each atom has a stable value each time that the formula is evaluated. An atom /// We assume that each atom has a stable value each time that the formula is evaluated. An atom
@ -197,7 +197,7 @@ use crate::Db;
/// allows us to perform simplifications like `A !A → true` and `A ∧ !A → false`. /// allows us to perform simplifications like `A !A → true` and `A ∧ !A → false`.
/// ///
/// That means that when you are constructing a formula, you might need to create distinct atoms /// That means that when you are constructing a formula, you might need to create distinct atoms
/// for a particular [`Constraint`], if your formula needs to consider how a particular runtime /// for a particular [`Predicate`], if your formula needs to consider how a particular runtime
/// property might be different at different points in the execution of the program. /// property might be different at different points in the execution of the program.
/// ///
/// Visibility constraints are normalized, so equivalent constraints are guaranteed to have equal /// Visibility constraints are normalized, so equivalent constraints are guaranteed to have equal
@ -225,7 +225,7 @@ impl std::fmt::Debug for ScopedVisibilityConstraintId {
// //
// There are 3 terminals, with hard-coded constraint IDs: true, ambiguous, and false. // There are 3 terminals, with hard-coded constraint IDs: true, ambiguous, and false.
// //
// _Atoms_ are the underlying Constraints, which are the variables that are evaluated by the // _Atoms_ are the underlying Predicates, which are the variables that are evaluated by the
// ternary function. // ternary function.
// //
// _Interior nodes_ provide the TDD structure for the formula. Interior nodes are stored in an // _Interior nodes_ provide the TDD structure for the formula. Interior nodes are stored in an
@ -234,9 +234,9 @@ impl std::fmt::Debug for ScopedVisibilityConstraintId {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct InteriorNode { struct InteriorNode {
/// A "variable" that is evaluated as part of a TDD ternary function. For visibility /// A "variable" that is evaluated as part of a TDD ternary function. For visibility
/// constraints, this is a `Constraint` that represents some runtime property of the Python /// constraints, this is a `Predicate` that represents some runtime property of the Python
/// code that we are evaluating. /// code that we are evaluating.
atom: ScopedConstraintId, atom: ScopedPredicateId,
if_true: ScopedVisibilityConstraintId, if_true: ScopedVisibilityConstraintId,
if_ambiguous: ScopedVisibilityConstraintId, if_ambiguous: ScopedVisibilityConstraintId,
if_false: ScopedVisibilityConstraintId, if_false: ScopedVisibilityConstraintId,
@ -343,23 +343,23 @@ impl VisibilityConstraintsBuilder {
.or_insert_with(|| self.interiors.push(node)) .or_insert_with(|| self.interiors.push(node))
} }
/// Adds a new visibility constraint that checks a single [`Constraint`]. /// Adds a new visibility constraint that checks a single [`Predicate`].
/// ///
/// [`ScopedConstraintId`]s are the “variables” that are evaluated by a TDD. A TDD variable has /// [`ScopedPredicateId`]s are the “variables” that are evaluated by a TDD. A TDD variable has
/// the same value no matter how many times it appears in the ternary formula that the TDD /// the same value no matter how many times it appears in the ternary formula that the TDD
/// represents. /// represents.
/// ///
/// However, we sometimes have to model how a `Constraint` can have a different runtime /// However, we sometimes have to model how a `Predicate` can have a different runtime
/// value at different points in the execution of the program. To handle this, you can take /// value at different points in the execution of the program. To handle this, you can take
/// advantage of the fact that the [`Constraints`] arena does not deduplicate `Constraint`s. /// advantage of the fact that the [`Predicates`] arena does not deduplicate `Predicate`s.
/// You can add a `Constraint` multiple times, yielding different `ScopedConstraintId`s, which /// You can add a `Predicate` multiple times, yielding different `ScopedPredicateId`s, which
/// you can then create separate TDD atoms for. /// you can then create separate TDD atoms for.
pub(crate) fn add_atom( pub(crate) fn add_atom(
&mut self, &mut self,
constraint: ScopedConstraintId, predicate: ScopedPredicateId,
) -> ScopedVisibilityConstraintId { ) -> ScopedVisibilityConstraintId {
self.add_interior(InteriorNode { self.add_interior(InteriorNode {
atom: constraint, atom: predicate,
if_true: ALWAYS_TRUE, if_true: ALWAYS_TRUE,
if_ambiguous: AMBIGUOUS, if_ambiguous: AMBIGUOUS,
if_false: ALWAYS_FALSE, if_false: ALWAYS_FALSE,
@ -534,7 +534,7 @@ impl VisibilityConstraints {
pub(crate) fn evaluate<'db>( pub(crate) fn evaluate<'db>(
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
constraints: &Constraints<'db>, predicates: &Predicates<'db>,
mut id: ScopedVisibilityConstraintId, mut id: ScopedVisibilityConstraintId,
) -> Truthiness { ) -> Truthiness {
loop { loop {
@ -544,8 +544,8 @@ impl VisibilityConstraints {
ALWAYS_FALSE => return Truthiness::AlwaysFalse, ALWAYS_FALSE => return Truthiness::AlwaysFalse,
_ => self.interiors[id], _ => self.interiors[id],
}; };
let constraint = &constraints[node.atom]; let predicate = &predicates[node.atom];
match Self::analyze_single(db, constraint) { match Self::analyze_single(db, predicate) {
Truthiness::AlwaysTrue => id = node.if_true, Truthiness::AlwaysTrue => id = node.if_true,
Truthiness::Ambiguous => id = node.if_ambiguous, Truthiness::Ambiguous => id = node.if_ambiguous,
Truthiness::AlwaysFalse => id = node.if_false, Truthiness::AlwaysFalse => id = node.if_false,
@ -553,14 +553,14 @@ impl VisibilityConstraints {
} }
} }
fn analyze_single(db: &dyn Db, constraint: &Constraint) -> Truthiness { fn analyze_single(db: &dyn Db, predicate: &Predicate) -> Truthiness {
match constraint.node { match predicate.node {
ConstraintNode::Expression(test_expr) => { PredicateNode::Expression(test_expr) => {
let ty = infer_expression_type(db, test_expr); let ty = infer_expression_type(db, test_expr);
ty.bool(db).negate_if(!constraint.is_positive) ty.bool(db).negate_if(!predicate.is_positive)
} }
ConstraintNode::Pattern(inner) => match inner.kind(db) { PredicateNode::Pattern(inner) => match inner.kind(db) {
PatternConstraintKind::Value(value, guard) => { PatternPredicateKind::Value(value, guard) => {
let subject_expression = inner.subject(db); let subject_expression = inner.subject(db);
let subject_ty = infer_expression_type(db, subject_expression); let subject_ty = infer_expression_type(db, subject_expression);
let value_ty = infer_expression_type(db, *value); let value_ty = infer_expression_type(db, *value);
@ -579,9 +579,9 @@ impl VisibilityConstraints {
Truthiness::Ambiguous Truthiness::Ambiguous
} }
} }
PatternConstraintKind::Singleton(..) PatternPredicateKind::Singleton(..)
| PatternConstraintKind::Class(..) | PatternPredicateKind::Class(..)
| PatternConstraintKind::Unsupported => Truthiness::Ambiguous, | PatternPredicateKind::Unsupported => Truthiness::Ambiguous,
}, },
} }
} }

View file

@ -538,7 +538,7 @@ fn symbol_from_bindings_impl<'db>(
bindings_with_constraints: BindingWithConstraintsIterator<'_, 'db>, bindings_with_constraints: BindingWithConstraintsIterator<'_, 'db>,
requires_explicit_reexport: RequiresExplicitReExport, requires_explicit_reexport: RequiresExplicitReExport,
) -> Symbol<'db> { ) -> Symbol<'db> {
let constraints = bindings_with_constraints.constraints; let predicates = bindings_with_constraints.predicates;
let visibility_constraints = bindings_with_constraints.visibility_constraints; let visibility_constraints = bindings_with_constraints.visibility_constraints;
let mut bindings_with_constraints = bindings_with_constraints.peekable(); let mut bindings_with_constraints = bindings_with_constraints.peekable();
@ -552,7 +552,7 @@ fn symbol_from_bindings_impl<'db>(
visibility_constraint, visibility_constraint,
narrowing_constraint: _, narrowing_constraint: _,
}) if binding.map_or(true, is_non_exported) => { }) if binding.map_or(true, is_non_exported) => {
visibility_constraints.evaluate(db, constraints, *visibility_constraint) visibility_constraints.evaluate(db, predicates, *visibility_constraint)
} }
_ => Truthiness::AlwaysFalse, _ => Truthiness::AlwaysFalse,
}; };
@ -570,7 +570,7 @@ fn symbol_from_bindings_impl<'db>(
} }
let static_visibility = let static_visibility =
visibility_constraints.evaluate(db, constraints, visibility_constraint); visibility_constraints.evaluate(db, predicates, visibility_constraint);
if static_visibility.is_always_false() { if static_visibility.is_always_false() {
return None; return None;
@ -629,7 +629,7 @@ fn symbol_from_declarations_impl<'db>(
declarations: DeclarationsIterator<'_, 'db>, declarations: DeclarationsIterator<'_, 'db>,
requires_explicit_reexport: RequiresExplicitReExport, requires_explicit_reexport: RequiresExplicitReExport,
) -> SymbolFromDeclarationsResult<'db> { ) -> SymbolFromDeclarationsResult<'db> {
let constraints = declarations.constraints; let predicates = declarations.predicates;
let visibility_constraints = declarations.visibility_constraints; let visibility_constraints = declarations.visibility_constraints;
let mut declarations = declarations.peekable(); let mut declarations = declarations.peekable();
@ -642,7 +642,7 @@ fn symbol_from_declarations_impl<'db>(
declaration, declaration,
visibility_constraint, visibility_constraint,
}) if declaration.map_or(true, is_non_exported) => { }) if declaration.map_or(true, is_non_exported) => {
visibility_constraints.evaluate(db, constraints, *visibility_constraint) visibility_constraints.evaluate(db, predicates, *visibility_constraint)
} }
_ => Truthiness::AlwaysFalse, _ => Truthiness::AlwaysFalse,
}; };
@ -659,7 +659,7 @@ fn symbol_from_declarations_impl<'db>(
} }
let static_visibility = let static_visibility =
visibility_constraints.evaluate(db, constraints, visibility_constraint); visibility_constraints.evaluate(db, predicates, visibility_constraint);
if static_visibility.is_always_false() { if static_visibility.is_always_false() {
None None

View file

@ -1,9 +1,9 @@
use crate::semantic_index::ast_ids::HasScopedExpressionId; use crate::semantic_index::ast_ids::HasScopedExpressionId;
use crate::semantic_index::constraint::{
Constraint, ConstraintNode, PatternConstraint, PatternConstraintKind,
};
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::semantic_index::expression::Expression; use crate::semantic_index::expression::Expression;
use crate::semantic_index::predicate::{
PatternPredicate, PatternPredicateKind, Predicate, PredicateNode,
};
use crate::semantic_index::symbol::{ScopeId, ScopedSymbolId, SymbolTable}; use crate::semantic_index::symbol::{ScopeId, ScopedSymbolId, SymbolTable};
use crate::semantic_index::symbol_table; use crate::semantic_index::symbol_table;
use crate::types::infer::infer_same_file_expression_type; use crate::types::infer::infer_same_file_expression_type;
@ -37,18 +37,18 @@ use std::sync::Arc;
/// constraint is applied to that definition, so we'd just return `None`. /// constraint is applied to that definition, so we'd just return `None`.
pub(crate) fn infer_narrowing_constraint<'db>( pub(crate) fn infer_narrowing_constraint<'db>(
db: &'db dyn Db, db: &'db dyn Db,
constraint: Constraint<'db>, predicate: Predicate<'db>,
definition: Definition<'db>, definition: Definition<'db>,
) -> Option<Type<'db>> { ) -> Option<Type<'db>> {
let constraints = match constraint.node { let constraints = match predicate.node {
ConstraintNode::Expression(expression) => { PredicateNode::Expression(expression) => {
if constraint.is_positive { if predicate.is_positive {
all_narrowing_constraints_for_expression(db, expression) all_narrowing_constraints_for_expression(db, expression)
} else { } else {
all_negative_narrowing_constraints_for_expression(db, expression) all_negative_narrowing_constraints_for_expression(db, expression)
} }
} }
ConstraintNode::Pattern(pattern) => all_narrowing_constraints_for_pattern(db, pattern), PredicateNode::Pattern(pattern) => all_narrowing_constraints_for_pattern(db, pattern),
}; };
if let Some(constraints) = constraints { if let Some(constraints) = constraints {
constraints.get(&definition.symbol(db)).copied() constraints.get(&definition.symbol(db)).copied()
@ -61,9 +61,9 @@ pub(crate) fn infer_narrowing_constraint<'db>(
#[salsa::tracked(return_ref)] #[salsa::tracked(return_ref)]
fn all_narrowing_constraints_for_pattern<'db>( fn all_narrowing_constraints_for_pattern<'db>(
db: &'db dyn Db, db: &'db dyn Db,
pattern: PatternConstraint<'db>, pattern: PatternPredicate<'db>,
) -> Option<NarrowingConstraints<'db>> { ) -> Option<NarrowingConstraints<'db>> {
NarrowingConstraintsBuilder::new(db, ConstraintNode::Pattern(pattern), true).finish() NarrowingConstraintsBuilder::new(db, PredicateNode::Pattern(pattern), true).finish()
} }
#[allow(clippy::ref_option)] #[allow(clippy::ref_option)]
@ -72,7 +72,7 @@ fn all_narrowing_constraints_for_expression<'db>(
db: &'db dyn Db, db: &'db dyn Db,
expression: Expression<'db>, expression: Expression<'db>,
) -> Option<NarrowingConstraints<'db>> { ) -> Option<NarrowingConstraints<'db>> {
NarrowingConstraintsBuilder::new(db, ConstraintNode::Expression(expression), true).finish() NarrowingConstraintsBuilder::new(db, PredicateNode::Expression(expression), true).finish()
} }
#[allow(clippy::ref_option)] #[allow(clippy::ref_option)]
@ -81,7 +81,7 @@ fn all_negative_narrowing_constraints_for_expression<'db>(
db: &'db dyn Db, db: &'db dyn Db,
expression: Expression<'db>, expression: Expression<'db>,
) -> Option<NarrowingConstraints<'db>> { ) -> Option<NarrowingConstraints<'db>> {
NarrowingConstraintsBuilder::new(db, ConstraintNode::Expression(expression), false).finish() NarrowingConstraintsBuilder::new(db, PredicateNode::Expression(expression), false).finish()
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -166,25 +166,25 @@ fn merge_constraints_or<'db>(
struct NarrowingConstraintsBuilder<'db> { struct NarrowingConstraintsBuilder<'db> {
db: &'db dyn Db, db: &'db dyn Db,
constraint: ConstraintNode<'db>, predicate: PredicateNode<'db>,
is_positive: bool, is_positive: bool,
} }
impl<'db> NarrowingConstraintsBuilder<'db> { impl<'db> NarrowingConstraintsBuilder<'db> {
fn new(db: &'db dyn Db, constraint: ConstraintNode<'db>, is_positive: bool) -> Self { fn new(db: &'db dyn Db, predicate: PredicateNode<'db>, is_positive: bool) -> Self {
Self { Self {
db, db,
constraint, predicate,
is_positive, is_positive,
} }
} }
fn finish(mut self) -> Option<NarrowingConstraints<'db>> { fn finish(mut self) -> Option<NarrowingConstraints<'db>> {
let constraints: Option<NarrowingConstraints<'db>> = match self.constraint { let constraints: Option<NarrowingConstraints<'db>> = match self.predicate {
ConstraintNode::Expression(expression) => { PredicateNode::Expression(expression) => {
self.evaluate_expression_constraint(expression, self.is_positive) self.evaluate_expression_predicate(expression, self.is_positive)
} }
ConstraintNode::Pattern(pattern) => self.evaluate_pattern_constraint(pattern), PredicateNode::Pattern(pattern) => self.evaluate_pattern_predicate(pattern),
}; };
if let Some(mut constraints) = constraints { if let Some(mut constraints) = constraints {
constraints.shrink_to_fit(); constraints.shrink_to_fit();
@ -194,16 +194,16 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
} }
} }
fn evaluate_expression_constraint( fn evaluate_expression_predicate(
&mut self, &mut self,
expression: Expression<'db>, expression: Expression<'db>,
is_positive: bool, is_positive: bool,
) -> Option<NarrowingConstraints<'db>> { ) -> Option<NarrowingConstraints<'db>> {
let expression_node = expression.node_ref(self.db).node(); let expression_node = expression.node_ref(self.db).node();
self.evaluate_expression_node_constraint(expression_node, expression, is_positive) self.evaluate_expression_node_predicate(expression_node, expression, is_positive)
} }
fn evaluate_expression_node_constraint( fn evaluate_expression_node_predicate(
&mut self, &mut self,
expression_node: &ruff_python_ast::Expr, expression_node: &ruff_python_ast::Expr,
expression: Expression<'db>, expression: Expression<'db>,
@ -217,28 +217,29 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
ast::Expr::Call(expr_call) => { ast::Expr::Call(expr_call) => {
self.evaluate_expr_call(expr_call, expression, is_positive) self.evaluate_expr_call(expr_call, expression, is_positive)
} }
ast::Expr::UnaryOp(unary_op) if unary_op.op == ast::UnaryOp::Not => self ast::Expr::UnaryOp(unary_op) if unary_op.op == ast::UnaryOp::Not => {
.evaluate_expression_node_constraint(&unary_op.operand, expression, !is_positive), self.evaluate_expression_node_predicate(&unary_op.operand, expression, !is_positive)
}
ast::Expr::BoolOp(bool_op) => self.evaluate_bool_op(bool_op, expression, is_positive), ast::Expr::BoolOp(bool_op) => self.evaluate_bool_op(bool_op, expression, is_positive),
_ => None, // TODO other test expression kinds _ => None, // TODO other test expression kinds
} }
} }
fn evaluate_pattern_constraint( fn evaluate_pattern_predicate(
&mut self, &mut self,
pattern: PatternConstraint<'db>, pattern: PatternPredicate<'db>,
) -> Option<NarrowingConstraints<'db>> { ) -> Option<NarrowingConstraints<'db>> {
let subject = pattern.subject(self.db); let subject = pattern.subject(self.db);
match pattern.kind(self.db) { match pattern.kind(self.db) {
PatternConstraintKind::Singleton(singleton, _guard) => { PatternPredicateKind::Singleton(singleton, _guard) => {
self.evaluate_match_pattern_singleton(subject, *singleton) self.evaluate_match_pattern_singleton(subject, *singleton)
} }
PatternConstraintKind::Class(cls, _guard) => { PatternPredicateKind::Class(cls, _guard) => {
self.evaluate_match_pattern_class(subject, *cls) self.evaluate_match_pattern_class(subject, *cls)
} }
// TODO: support more pattern kinds // TODO: support more pattern kinds
PatternConstraintKind::Value(..) | PatternConstraintKind::Unsupported => None, PatternPredicateKind::Value(..) | PatternPredicateKind::Unsupported => None,
} }
} }
@ -247,9 +248,9 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
} }
fn scope(&self) -> ScopeId<'db> { fn scope(&self) -> ScopeId<'db> {
match self.constraint { match self.predicate {
ConstraintNode::Expression(expression) => expression.scope(self.db), PredicateNode::Expression(expression) => expression.scope(self.db),
ConstraintNode::Pattern(pattern) => pattern.scope(self.db), PredicateNode::Pattern(pattern) => pattern.scope(self.db),
} }
} }
@ -456,7 +457,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
&& expr_call.arguments.keywords.is_empty() && expr_call.arguments.keywords.is_empty()
&& class_type.class().is_known(self.db, KnownClass::Bool) => && class_type.class().is_known(self.db, KnownClass::Bool) =>
{ {
self.evaluate_expression_node_constraint( self.evaluate_expression_node_predicate(
&expr_call.arguments.args[0], &expr_call.arguments.args[0],
expression, expression,
is_positive, is_positive,
@ -528,7 +529,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
} }
}) })
.map(|sub_expr| { .map(|sub_expr| {
self.evaluate_expression_node_constraint(sub_expr, expression, is_positive) self.evaluate_expression_node_predicate(sub_expr, expression, is_positive)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
match (expr_bool_op.op, is_positive) { match (expr_bool_op.op, is_positive) {