Skip to content

Commit 126fc80

Browse files
committed
Stop deduplicating constraints
1 parent 69e15a6 commit 126fc80

File tree

4 files changed

+25
-74
lines changed

4 files changed

+25
-74
lines changed

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ impl<'db> SemanticIndexBuilder<'db> {
457457
let constraint_id = self.current_use_def_map_mut().add_constraint(constraint);
458458
let id = self
459459
.current_visibility_constraints_mut()
460-
.add_atom(constraint_id, 0);
460+
.add_atom(constraint_id);
461461
self.record_visibility_constraint_id(id);
462462
id
463463
}
@@ -1187,13 +1187,14 @@ where
11871187
// We need multiple copies of the visibility constraint for the while condition,
11881188
// since we need to model situations where the first evaluation of the condition
11891189
// returns True, but a later evaluation returns False.
1190-
let constraint_id = self.current_use_def_map_mut().add_constraint(constraint);
1190+
let first_constraint_id = self.current_use_def_map_mut().add_constraint(constraint);
1191+
let later_constraint_id = self.current_use_def_map_mut().add_constraint(constraint);
11911192
let first_vis_constraint_id = self
11921193
.current_visibility_constraints_mut()
1193-
.add_atom(constraint_id, 0);
1194+
.add_atom(first_constraint_id);
11941195
let later_vis_constraint_id = self
11951196
.current_visibility_constraints_mut()
1196-
.add_atom(constraint_id, 1);
1197+
.add_atom(later_constraint_id);
11971198

11981199
// Save aside any break states from an outer loop
11991200
let saved_break_states = std::mem::take(&mut self.loop_break_states);
@@ -1780,7 +1781,7 @@ where
17801781
};
17811782
let visibility_constraint = self
17821783
.current_visibility_constraints_mut()
1783-
.add_atom(constraint_id, 0);
1784+
.add_atom(constraint_id);
17841785

17851786
let after_expr = self.flow_snapshot();
17861787

crates/red_knot_python_semantic/src/semantic_index/constraint.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use ruff_db::files::File;
22
use ruff_index::{newtype_index, IndexVec};
33
use ruff_python_ast::Singleton;
4-
use rustc_hash::FxHashMap;
54

65
use crate::db::Db;
76
use crate::semantic_index::expression::Expression;
@@ -18,16 +17,12 @@ pub(crate) type Constraints<'db> = IndexVec<ScopedConstraintId, Constraint<'db>>
1817
#[derive(Debug, Default)]
1918
pub(crate) struct ConstraintsBuilder<'db> {
2019
constraints: IndexVec<ScopedConstraintId, Constraint<'db>>,
21-
constraint_cache: FxHashMap<Constraint<'db>, ScopedConstraintId>,
2220
}
2321

2422
impl<'db> ConstraintsBuilder<'db> {
2523
/// Adds a constraint, ensuring that we only store any particular constraint once.
2624
pub(crate) fn add_constraint(&mut self, constraint: Constraint<'db>) -> ScopedConstraintId {
27-
*self
28-
.constraint_cache
29-
.entry(constraint)
30-
.or_insert_with(|| self.constraints.push(constraint))
25+
self.constraints.push(constraint)
3126
}
3227

3328
pub(crate) fn build(mut self) -> Constraints<'db> {

crates/red_knot_python_semantic/src/visibility_constraints.rs

Lines changed: 17 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -236,60 +236,15 @@ impl std::fmt::Debug for ScopedVisibilityConstraintId {
236236

237237
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
238238
struct InteriorNode {
239-
atom: Atom,
239+
/// A "variable" that is evaluated as part of a TDD ternary function. For visibility
240+
/// constraints, this is a `Constraint` that represents some runtime property of the Python
241+
/// code that we are evaluating.
242+
atom: ScopedConstraintId,
240243
if_true: ScopedVisibilityConstraintId,
241244
if_ambiguous: ScopedVisibilityConstraintId,
242245
if_false: ScopedVisibilityConstraintId,
243246
}
244247

245-
/// A "variable" that is evaluated as part of a TDD ternary function. For visibility constraints,
246-
/// this is a `Constraint` that represents some runtime property of the Python code that we are
247-
/// evaluating. We intern these constraints in an arena ([`VisibilityConstraints::constraints`]).
248-
/// An atom is then an index into this arena.
249-
///
250-
/// By using a 32-bit index, we would typically allow 4 billion distinct constraints within a
251-
/// scope. However, we sometimes have to model how a `Constraint` can have a different runtime
252-
/// value at different points in the execution of the program. To handle this, we reserve the top
253-
/// byte of an atom to represent a "copy number". This is just an opaque value that allows
254-
/// different `Atom`s to evaluate the same `Constraint`. This yields a maximum of 16 million
255-
/// distinct `Constraint`s in a scope, and 256 possible copies of each of those constraints.
256-
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
257-
struct Atom(u32);
258-
259-
impl Atom {
260-
/// Deconstruct an atom into a constraint index and a copy number.
261-
#[inline]
262-
fn into_index_and_copy(self) -> (ScopedConstraintId, u8) {
263-
let copy = self.0 >> 24;
264-
let index = self.0 & 0x00ff_ffff;
265-
(index.into(), copy as u8)
266-
}
267-
268-
#[inline]
269-
fn copy_of(mut self, copy: u8) -> Self {
270-
// Clear out the previous copy number
271-
self.0 &= 0x00ff_ffff;
272-
// OR in the new one
273-
self.0 |= u32::from(copy) << 24;
274-
self
275-
}
276-
}
277-
278-
impl From<ScopedConstraintId> for Atom {
279-
fn from(constraint: ScopedConstraintId) -> Atom {
280-
Atom(constraint.as_u32())
281-
}
282-
}
283-
284-
// A custom Debug implementation that prints out the constraint index and copy number as distinct
285-
// fields.
286-
impl std::fmt::Debug for Atom {
287-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288-
let (index, copy) = self.into_index_and_copy();
289-
f.debug_tuple("Atom").field(&index).field(&copy).finish()
290-
}
291-
}
292-
293248
impl ScopedVisibilityConstraintId {
294249
/// A special ID that is used for an "always true" / "always visible" constraint.
295250
pub(crate) const ALWAYS_TRUE: ScopedVisibilityConstraintId =
@@ -377,11 +332,6 @@ impl VisibilityConstraintsBuilder {
377332
}
378333
}
379334

380-
/// Adds a constraint, ensuring that we only store any particular constraint once.
381-
fn add_constraint(&mut self, constraint: ScopedConstraintId, copy: u8) -> Atom {
382-
Atom::from(constraint).copy_of(copy)
383-
}
384-
385335
/// Adds an interior node, ensuring that we always use the same visibility constraint ID for
386336
/// equal nodes.
387337
fn add_interior(&mut self, node: InteriorNode) -> ScopedVisibilityConstraintId {
@@ -397,17 +347,23 @@ impl VisibilityConstraintsBuilder {
397347
.or_insert_with(|| self.interiors.push(node))
398348
}
399349

400-
/// Adds a new visibility constraint that checks a single [`Constraint`]. Provide different
401-
/// values for `copy` if you need to model that the constraint can evaluate to different
402-
/// results at different points in the execution of the program being modeled.
350+
/// Adds a new visibility constraint that checks a single [`Constraint`].
351+
///
352+
/// [`ScopedConstraintId`]s are the “variables” that are evaluated by a TDD. A TDD variable has
353+
/// the same value no matter how many times it appears in the ternary formula that the TDD
354+
/// represents.
355+
///
356+
/// However, we sometimes have to model how a `Constraint` can have a different runtime
357+
/// value at different points in the execution of the program. To handle this, you can take
358+
/// advantage of the fact that the [`Constraints`] arena does not deduplicate `Constraint`s.
359+
/// You can add a `Constraint` multiple times, yielding different `ScopeConstraintId`s, which
360+
/// you can then create separate TDD atoms for.
403361
pub(crate) fn add_atom(
404362
&mut self,
405363
constraint: ScopedConstraintId,
406-
copy: u8,
407364
) -> ScopedVisibilityConstraintId {
408-
let atom = self.add_constraint(constraint, copy);
409365
self.add_interior(InteriorNode {
410-
atom,
366+
atom: constraint,
411367
if_true: ALWAYS_TRUE,
412368
if_ambiguous: AMBIGUOUS,
413369
if_false: ALWAYS_FALSE,
@@ -592,8 +548,7 @@ impl VisibilityConstraints {
592548
ALWAYS_FALSE => return Truthiness::AlwaysFalse,
593549
_ => self.interiors[id],
594550
};
595-
let (constraint_id, _) = node.atom.into_index_and_copy();
596-
let constraint = &constraints[constraint_id];
551+
let constraint = &constraints[node.atom];
597552
match Self::analyze_single(db, constraint) {
598553
Truthiness::AlwaysTrue => id = node.if_true,
599554
Truthiness::Ambiguous => id = node.if_ambiguous,

crates/ruff_macros/src/newtype_index.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub(super) fn generate_newtype_index(item: ItemStruct) -> syn::Result<proc_macro
3232
let semi_token = semi_token.unwrap_or_default();
3333
let output = quote! {
3434
#(#attrs)*
35-
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
35+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
3636
#vis #struct_token #ident(std::num::NonZeroU32)#semi_token
3737

3838
impl #ident {

0 commit comments

Comments
 (0)