Skip to content

Commit 771251e

Browse files
committed
Merge branch 'dcreager/predicates' into dcreager/alist-for-live-bds
* dcreager/predicates: fix doc links Rename constraint to predicate
2 parents 7b35edf + b3e10ed commit 771251e

File tree

10 files changed

+345
-313
lines changed

10 files changed

+345
-313
lines changed

crates/red_knot_python_semantic/src/semantic_index.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ use crate::Db;
2525
pub mod ast_ids;
2626
pub mod attribute_assignment;
2727
mod builder;
28-
pub(crate) mod constraint;
2928
pub mod definition;
3029
pub mod expression;
3130
mod narrowing_constraints;
31+
pub(crate) mod predicate;
3232
pub mod symbol;
3333
mod use_def;
3434
mod visibility_constraints;

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 80 additions & 70 deletions
Large diffs are not rendered by default.

crates/red_knot_python_semantic/src/semantic_index/constraint.rs

Lines changed: 0 additions & 77 deletions
This file was deleted.

crates/red_knot_python_semantic/src/semantic_index/narrowing_constraints.rs

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,77 @@
11
//! # Narrowing constraints
22
//!
3-
//! When building a semantic index for a file, we associate each binding with _narrowing
4-
//! constraints_. The narrowing constraint is used to constrain the type of the binding's symbol.
5-
//! Note that a binding can be associated with a different narrowing constraint at different points
6-
//! in a file. See the [`use_def`][crate::semantic_index::use_def] module for more details.
3+
//! When building a semantic index for a file, we associate each binding with a _narrowing
4+
//! constraint_, which constrains the type of the binding's symbol. Note that a binding can be
5+
//! associated with a different narrowing constraint at different points in a file. See the
6+
//! [`use_def`][crate::semantic_index::use_def] module for more details.
77
//!
88
//! This module defines how narrowing constraints are stored internally.
99
//!
10-
//! A _narrowing constraint_ consists of a list of _clauses_, each of which corresponds with an
11-
//! expression in the source file (represented by a [`Constraint`]). We need to support the
10+
//! A _narrowing constraint_ consists of a list of _predicates_, each of which corresponds with an
11+
//! expression in the source file (represented by a [`Predicate`]). We need to support the
1212
//! following operations on narrowing constraints:
1313
//!
14-
//! - Adding a new clause to an existing constraint
15-
//! - Merging two constraints together, which produces the _intersection_ of their clauses
16-
//! - Iterating through the clauses in a constraint
14+
//! - Adding a new predicate to an existing constraint
15+
//! - Merging two constraints together, which produces the _intersection_ of their predicates
16+
//! - Iterating through the predicates in a constraint
1717
//!
18-
//! In particular, note that we do not need random access to the clauses in a constraint. That
18+
//! In particular, note that we do not need random access to the predicates in a constraint. That
1919
//! means that we can use a simple [_sorted association list_][ruff_index::list] as our data
2020
//! structure. That lets us use a single 32-bit integer to store each narrowing constraint, no
21-
//! matter how many clauses it contains. It also makes merging two narrowing constraints fast,
21+
//! matter how many predicates it contains. It also makes merging two narrowing constraints fast,
2222
//! since alists support fast intersection.
2323
//!
2424
//! Because we visit the contents of each scope in source-file order, and assign scoped IDs in
2525
//! source-file order, that means that we will tend to visit narrowing constraints in order by
26-
//! their IDs. This is exactly how to get the best performance from our alist implementation.
26+
//! their predicate IDs. This is exactly how to get the best performance from our alist
27+
//! implementation.
2728
//!
28-
//! [`Constraint`]: crate::semantic_index::constraint::Constraint
29+
//! [`Predicate`]: crate::semantic_index::predicate::Predicate
2930
3031
use ruff_index::list::{ListBuilder, ListSetReverseIterator, ListStorage};
3132
use ruff_index::newtype_index;
3233

33-
use crate::semantic_index::constraint::ScopedConstraintId;
34+
use crate::semantic_index::predicate::ScopedPredicateId;
3435

3536
/// A narrowing constraint associated with a live binding.
3637
///
37-
/// A constraint is a list of clauses, each of which is a [`Constraint`] that constrains the type
38-
/// of the binding's symbol.
38+
/// A constraint is a list of [`Predicate`]s that each constrain the type of the binding's symbol.
3939
///
4040
/// An instance of this type represents a _non-empty_ narrowing constraint. You will often wrap
4141
/// this in `Option` and use `None` to represent an empty narrowing constraint.
4242
///
43-
/// [`Constraint`]: crate::semantic_index::constraint::Constraint
43+
/// [`Predicate`]: crate::semantic_index::predicate::Predicate
4444
#[newtype_index]
4545
pub(crate) struct ScopedNarrowingConstraintId;
4646

47-
/// One of the clauses in a narrowing constraint, which is a [`Constraint`] that constrains the
48-
/// type of the binding's symbol.
47+
/// One of the [`Predicate`]s in a narrowing constraint, which constraints the type of the
48+
/// binding's symbol.
4949
///
50-
/// Note that those [`Constraint`]s are stored in [their own per-scope
51-
/// arena][crate::semantic_index::constraint::Constraints], so internally we use a
52-
/// [`ScopedConstraintId`] to refer to the underlying constraint.
50+
/// Note that those [`Predicate`]s are stored in [their own per-scope
51+
/// arena][crate::semantic_index::predicate::Predicates], so internally we use a
52+
/// [`ScopedPredicateId`] to refer to the underlying predicate.
5353
///
54-
/// [`Constraint`]: crate::semantic_index::constraint::Constraint
54+
/// [`Predicate`]: crate::semantic_index::predicate::Predicate
5555
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
56-
pub(crate) struct ScopedNarrowingConstraintClause(ScopedConstraintId);
56+
pub(crate) struct ScopedNarrowingConstraintPredicate(ScopedPredicateId);
5757

58-
impl ScopedNarrowingConstraintClause {
59-
/// Returns (the ID of) the `Constraint` for this clause
60-
pub(crate) fn constraint(self) -> ScopedConstraintId {
58+
impl ScopedNarrowingConstraintPredicate {
59+
/// Returns (the ID of) the `Predicate`
60+
pub(crate) fn predicate(self) -> ScopedPredicateId {
6161
self.0
6262
}
6363
}
6464

65-
impl From<ScopedConstraintId> for ScopedNarrowingConstraintClause {
66-
fn from(constraint: ScopedConstraintId) -> ScopedNarrowingConstraintClause {
67-
ScopedNarrowingConstraintClause(constraint)
65+
impl From<ScopedPredicateId> for ScopedNarrowingConstraintPredicate {
66+
fn from(predicate: ScopedPredicateId) -> ScopedNarrowingConstraintPredicate {
67+
ScopedNarrowingConstraintPredicate(predicate)
6868
}
6969
}
7070

7171
/// A collection of narrowing constraints for a given scope.
7272
#[derive(Debug, Eq, PartialEq)]
7373
pub(crate) struct NarrowingConstraints {
74-
lists: ListStorage<ScopedNarrowingConstraintId, ScopedNarrowingConstraintClause>,
74+
lists: ListStorage<ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate>,
7575
}
7676

7777
// Building constraints
@@ -80,7 +80,7 @@ pub(crate) struct NarrowingConstraints {
8080
/// A builder for creating narrowing constraints.
8181
#[derive(Debug, Default, Eq, PartialEq)]
8282
pub(crate) struct NarrowingConstraintsBuilder {
83-
lists: ListBuilder<ScopedNarrowingConstraintId, ScopedNarrowingConstraintClause>,
83+
lists: ListBuilder<ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate>,
8484
}
8585

8686
impl NarrowingConstraintsBuilder {
@@ -90,18 +90,18 @@ impl NarrowingConstraintsBuilder {
9090
}
9191
}
9292

93-
/// Adds a clause to an existing narrowing constraint.
94-
pub(crate) fn add(
93+
/// Adds a predicate to an existing narrowing constraint.
94+
pub(crate) fn add_predicate_to_constraint(
9595
&mut self,
9696
constraint: Option<ScopedNarrowingConstraintId>,
97-
clause: ScopedNarrowingConstraintClause,
97+
predicate: ScopedNarrowingConstraintPredicate,
9898
) -> Option<ScopedNarrowingConstraintId> {
99-
self.lists.insert(constraint, clause)
99+
self.lists.insert(constraint, predicate)
100100
}
101101

102-
/// Returns the intersection of two narrowing constraints. The result contains the clauses that
103-
/// appear in both inputs.
104-
pub(crate) fn intersect(
102+
/// Returns the intersection of two narrowing constraints. The result contains the predicates
103+
/// that appear in both inputs.
104+
pub(crate) fn intersect_constraints(
105105
&mut self,
106106
a: Option<ScopedNarrowingConstraintId>,
107107
b: Option<ScopedNarrowingConstraintId>,
@@ -114,12 +114,12 @@ impl NarrowingConstraintsBuilder {
114114
// ---------
115115

116116
pub(crate) type NarrowingConstraintsIterator<'a> = std::iter::Copied<
117-
ListSetReverseIterator<'a, ScopedNarrowingConstraintId, ScopedNarrowingConstraintClause>,
117+
ListSetReverseIterator<'a, ScopedNarrowingConstraintId, ScopedNarrowingConstraintPredicate>,
118118
>;
119119

120120
impl NarrowingConstraints {
121-
/// Iterates over the clauses in a narrowing constraint.
122-
pub(crate) fn iter_clauses(
121+
/// Iterates over the predicates in a narrowing constraint.
122+
pub(crate) fn iter_predicates(
123123
&self,
124124
set: Option<ScopedNarrowingConstraintId>,
125125
) -> NarrowingConstraintsIterator<'_> {
@@ -134,14 +134,14 @@ impl NarrowingConstraints {
134134
mod tests {
135135
use super::*;
136136

137-
impl ScopedNarrowingConstraintClause {
137+
impl ScopedNarrowingConstraintPredicate {
138138
pub(crate) fn as_u32(self) -> u32 {
139139
self.0.as_u32()
140140
}
141141
}
142142

143143
impl NarrowingConstraintsBuilder {
144-
pub(crate) fn iter_constraints(
144+
pub(crate) fn iter_predicates(
145145
&self,
146146
set: Option<ScopedNarrowingConstraintId>,
147147
) -> NarrowingConstraintsIterator<'_> {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//! _Predicates_ are Python expressions whose runtime values can affect type inference.
2+
//!
3+
//! We currently use predicates in two places:
4+
//!
5+
//! - [_Narrowing constraints_][crate::semantic_index::narrowing_constraints] constrain the type of
6+
//! a binding that is visible at a particular use.
7+
//! - [_Visibility constraints_][crate::semantic_index::visibility_constraints] determine the
8+
//! static visibility of a binding, and the reachability of a statement.
9+
10+
use ruff_db::files::File;
11+
use ruff_index::{newtype_index, IndexVec};
12+
use ruff_python_ast::Singleton;
13+
14+
use crate::db::Db;
15+
use crate::semantic_index::expression::Expression;
16+
use crate::semantic_index::symbol::{FileScopeId, ScopeId};
17+
18+
// A scoped identifier for each `Predicate` in a scope.
19+
#[newtype_index]
20+
#[derive(Ord, PartialOrd)]
21+
pub(crate) struct ScopedPredicateId;
22+
23+
// A collection of predicates for a given scope.
24+
pub(crate) type Predicates<'db> = IndexVec<ScopedPredicateId, Predicate<'db>>;
25+
26+
#[derive(Debug, Default)]
27+
pub(crate) struct PredicatesBuilder<'db> {
28+
predicates: IndexVec<ScopedPredicateId, Predicate<'db>>,
29+
}
30+
31+
impl<'db> PredicatesBuilder<'db> {
32+
/// Adds a predicate. Note that we do not deduplicate predicates. If you add a `Predicate`
33+
/// more than once, you will get distinct `ScopedPredicateId`s for each one. (This lets you
34+
/// model predicates that might evaluate to different values at different points of execution.)
35+
pub(crate) fn add_predicate(&mut self, predicate: Predicate<'db>) -> ScopedPredicateId {
36+
self.predicates.push(predicate)
37+
}
38+
39+
pub(crate) fn build(mut self) -> Predicates<'db> {
40+
self.predicates.shrink_to_fit();
41+
self.predicates
42+
}
43+
}
44+
45+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
46+
pub(crate) struct Predicate<'db> {
47+
pub(crate) node: PredicateNode<'db>,
48+
pub(crate) is_positive: bool,
49+
}
50+
51+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
52+
pub(crate) enum PredicateNode<'db> {
53+
Expression(Expression<'db>),
54+
Pattern(PatternPredicate<'db>),
55+
}
56+
57+
/// Pattern kinds for which we support type narrowing and/or static visibility analysis.
58+
#[derive(Debug, Clone, Hash, PartialEq, salsa::Update)]
59+
pub(crate) enum PatternPredicateKind<'db> {
60+
Singleton(Singleton, Option<Expression<'db>>),
61+
Value(Expression<'db>, Option<Expression<'db>>),
62+
Class(Expression<'db>, Option<Expression<'db>>),
63+
Unsupported,
64+
}
65+
66+
#[salsa::tracked]
67+
pub(crate) struct PatternPredicate<'db> {
68+
pub(crate) file: File,
69+
70+
pub(crate) file_scope: FileScopeId,
71+
72+
pub(crate) subject: Expression<'db>,
73+
74+
#[return_ref]
75+
pub(crate) kind: PatternPredicateKind<'db>,
76+
77+
count: countme::Count<PatternPredicate<'static>>,
78+
}
79+
80+
impl<'db> PatternPredicate<'db> {
81+
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
82+
self.file_scope(db).to_scope_id(db, self.file(db))
83+
}
84+
}

0 commit comments

Comments
 (0)