177177//!
178178//! There is another special kind of possible "definition" for a place: there might be a path from
179179//! the scope entry to a given use in which the place is never bound. We model this with a special
180- //! "unbound/undeclared" definition (a [`DefinitionState::Undefined`] entry at the start of the
181- //! `all_definitions` vector). If that sentinel definition is present in the live bindings at a
182- //! given use, it means that there is a possible path through control flow in which that place is
183- //! unbound. Similarly, if that sentinel is present in the live declarations, it means that the
184- //! place is (possibly) undeclared.
180+ //! "unbound/undeclared" definition at logical index zero. If that sentinel definition is present
181+ //! in the live bindings at a given use, it means that there is a possible path through control
182+ //! flow in which that place is unbound. Similarly, if that sentinel is present in the live
183+ //! declarations, it means that the place is (possibly) undeclared.
185184//!
186185//! To build a [`UseDefMap`], the [`UseDefMapBuilder`] is notified of each new use, definition, and
187186//! constraint as they are encountered by the
@@ -245,7 +244,7 @@ use std::ops::Index;
245244use std:: rc:: Rc ;
246245use std:: sync:: LazyLock ;
247246
248- use ruff_index:: { FrozenIndexVec , Idx , IndexSlice , IndexVec , newtype_index} ;
247+ use ruff_index:: { FrozenIndexVec , Idx , IndexVec , newtype_index} ;
249248use ruff_text_size:: TextRange ;
250249use rustc_hash:: { FxBuildHasher , FxHashMap , FxHasher } ;
251250use smallvec:: SmallVec ;
@@ -667,12 +666,66 @@ impl<'db> RetainedDefinitionState<'db> {
667666
668667static_assertions:: assert_eq_size!( RetainedDefinitionState <' static >, DefinitionState <' static >) ;
669668
669+ /// Retained definition states, excluding the implicit unbound definition at index zero.
670+ #[ derive( Debug , PartialEq , Eq , salsa:: Update , get_size2:: GetSize ) ]
671+ struct RetainedDefinitions < ' db > {
672+ states : Box < [ RetainedDefinitionState < ' db > ] > ,
673+ }
674+
675+ impl < ' db > RetainedDefinitions < ' db > {
676+ fn new (
677+ states : IndexVec < ScopedDefinitionId , DefinitionState < ' db > > ,
678+ used : IndexVec < ScopedDefinitionId , bool > ,
679+ ) -> Self {
680+ let mut states = states. into_iter ( ) ;
681+ let mut used = used. into_iter ( ) ;
682+
683+ let unbound_state = states. next ( ) ;
684+ let unbound_used = used. next ( ) ;
685+ debug_assert_eq ! ( unbound_state, Some ( DefinitionState :: Undefined ) ) ;
686+ debug_assert_eq ! ( unbound_used, Some ( false ) ) ;
687+
688+ Self {
689+ states : states
690+ . zip ( used)
691+ . map ( |( state, used) | RetainedDefinitionState :: new ( state, used) )
692+ . collect ( ) ,
693+ }
694+ }
695+
696+ #[ inline]
697+ fn get ( & self , id : ScopedDefinitionId ) -> RetainedDefinitionState < ' db > {
698+ let index = id. index ( ) ;
699+ if index == 0 {
700+ RetainedDefinitionState :: Undefined
701+ } else {
702+ self . states [ index - 1 ]
703+ }
704+ }
705+
706+ fn iter_enumerated (
707+ & self ,
708+ ) -> impl Iterator < Item = ( ScopedDefinitionId , RetainedDefinitionState < ' db > ) > + ' _ {
709+ std:: iter:: once ( (
710+ ScopedDefinitionId :: UNBOUND ,
711+ RetainedDefinitionState :: Undefined ,
712+ ) )
713+ . chain (
714+ self . states
715+ . iter ( )
716+ . copied ( )
717+ . enumerate ( )
718+ . map ( |( index, state) | ( ScopedDefinitionId :: new ( index + 1 ) , state) ) ,
719+ )
720+ }
721+ }
722+
670723/// Applicable definitions and constraints for every use of a name.
671724#[ derive( Debug , PartialEq , Eq , salsa:: Update , get_size2:: GetSize ) ]
672725pub struct UseDefMap < ' db > {
673- /// Array of [` Definition`] in this scope. Only the first entry should be [`DefinitionState::Undefined`];
674- /// this represents the implicit "unbound"/"undeclared" definition of every place .
675- all_definitions : FrozenIndexVec < ScopedDefinitionId , RetainedDefinitionState < ' db > > ,
726+ /// Definition states in this scope, plus an implicit "unbound"/"undeclared" definition at
727+ /// index zero .
728+ all_definitions : RetainedDefinitions < ' db > ,
676729
677730 /// Constraint lookup tables, absent when all retained constraints are built-in terminal
678731 /// values that require no table lookup.
@@ -811,7 +864,7 @@ impl<'db> UseDefMap<'db> {
811864 ) -> impl Iterator < Item = ( ScopedDefinitionId , DefinitionState < ' db > , bool ) > + ' _ {
812865 self . all_definitions
813866 . iter_enumerated ( )
814- . map ( |( id, & state) | ( id, state. state ( ) , state. is_used ( ) ) )
867+ . map ( |( id, state) | ( id, state. state ( ) , state. is_used ( ) ) )
815868 }
816869
817870 pub fn bindings_at_use ( & self , use_id : ScopedUseId ) -> BindingWithConstraintsIterator < ' _ , ' db > {
@@ -872,7 +925,7 @@ impl<'db> UseDefMap<'db> {
872925 }
873926
874927 pub fn definition ( & self , id : ScopedDefinitionId ) -> DefinitionState < ' db > {
875- self . all_definitions [ id ] . state ( )
928+ self . all_definitions . get ( id ) . state ( )
876929 }
877930
878931 pub fn narrowing_evaluator (
@@ -1162,7 +1215,7 @@ type EnclosingSnapshots = IndexVec<ScopedEnclosingSnapshotId, EnclosingSnapshot>
11621215
11631216#[ derive( Clone , Debug ) ]
11641217pub struct BindingWithConstraintsIterator < ' map , ' db > {
1165- all_definitions : & ' map IndexSlice < ScopedDefinitionId , RetainedDefinitionState < ' db > > ,
1218+ all_definitions : & ' map RetainedDefinitions < ' db > ,
11661219 constraint_tables : & ' map ConstraintTables < ' db > ,
11671220 boundness_analysis : BoundnessAnalysis ,
11681221 inner : LiveBindingsIterator < ' map > ,
@@ -1189,7 +1242,7 @@ impl<'map, 'db> Iterator for BindingWithConstraintsIterator<'map, 'db> {
11891242 self . inner
11901243 . next ( )
11911244 . map ( |live_binding| BindingWithConstraints {
1192- binding : self . all_definitions [ live_binding. binding ( ) ] . state ( ) ,
1245+ binding : self . all_definitions . get ( live_binding. binding ( ) ) . state ( ) ,
11931246 binding_order : live_binding. binding ( ) ,
11941247 narrowing_constraint : NarrowingEvaluator {
11951248 constraint : live_binding. narrowing_constraint ( ) ,
@@ -1231,7 +1284,7 @@ impl<'map, 'db> NarrowingEvaluator<'map, 'db> {
12311284
12321285#[ derive( Clone ) ]
12331286pub struct DeclarationsIterator < ' map , ' db > {
1234- all_definitions : & ' map IndexSlice < ScopedDefinitionId , RetainedDefinitionState < ' db > > ,
1287+ all_definitions : & ' map RetainedDefinitions < ' db > ,
12351288 constraint_tables : & ' map ConstraintTables < ' db > ,
12361289 boundness_analysis : BoundnessAnalysis ,
12371290 inner : LiveDeclarationsIterator < ' map > ,
@@ -1269,7 +1322,7 @@ impl<'db> Iterator for DeclarationsIterator<'_, 'db> {
12691322 reachability_constraint,
12701323 } | {
12711324 DeclarationWithConstraint {
1272- declaration : self . all_definitions [ * declaration] . state ( ) ,
1325+ declaration : self . all_definitions . get ( * declaration) . state ( ) ,
12731326 declaration_order : * declaration,
12741327 reachability_constraint : * reachability_constraint,
12751328 }
@@ -2571,12 +2624,7 @@ impl<'db> UseDefMapBuilder<'db> {
25712624 narrowing_constraints,
25722625 } )
25732626 } ) ;
2574- let all_definitions = self
2575- . all_definitions
2576- . into_iter ( )
2577- . zip ( self . used_bindings )
2578- . map ( |( state, used) | RetainedDefinitionState :: new ( state, used) )
2579- . collect ( ) ;
2627+ let all_definitions = RetainedDefinitions :: new ( self . all_definitions , self . used_bindings ) ;
25802628
25812629 UseDefMap {
25822630 all_definitions,
0 commit comments