@@ -85,7 +85,7 @@ fn scope_cycle_recover<'db>(
8585}
8686
8787fn scope_cycle_initial < ' db > ( _db : & ' db dyn Db , scope : ScopeId < ' db > ) -> ScopeInference < ' db > {
88- ScopeInference :: cycle_fallback ( scope)
88+ ScopeInference :: cycle_initial ( scope)
8989}
9090
9191/// Infer all types for a [`Definition`] (including sub-expressions).
@@ -123,7 +123,7 @@ fn definition_cycle_initial<'db>(
123123 db : & ' db dyn Db ,
124124 definition : Definition < ' db > ,
125125) -> DefinitionInference < ' db > {
126- DefinitionInference :: cycle_fallback ( definition. scope ( db) )
126+ DefinitionInference :: cycle_initial ( definition. scope ( db) )
127127}
128128
129129/// Infer types for all deferred type expressions in a [`Definition`].
@@ -164,7 +164,7 @@ fn deferred_cycle_initial<'db>(
164164 db : & ' db dyn Db ,
165165 definition : Definition < ' db > ,
166166) -> DefinitionInference < ' db > {
167- DefinitionInference :: cycle_fallback ( definition. scope ( db) )
167+ DefinitionInference :: cycle_initial ( definition. scope ( db) )
168168}
169169
170170/// Infer all types for an [`Expression`] (including sub-expressions).
@@ -192,20 +192,29 @@ pub(crate) fn infer_expression_types<'db>(
192192 . finish_expression ( )
193193}
194194
195+ /// How many fixpoint iterations to allow before falling back to Divergent type.
196+ const ITERATIONS_BEFORE_FALLBACK : u32 = 10 ;
197+
195198fn expression_cycle_recover < ' db > (
196- _db : & ' db dyn Db ,
199+ db : & ' db dyn Db ,
197200 _value : & ExpressionInference < ' db > ,
198- _count : u32 ,
199- _expression : Expression < ' db > ,
201+ count : u32 ,
202+ expression : Expression < ' db > ,
200203) -> salsa:: CycleRecoveryAction < ExpressionInference < ' db > > {
201- salsa:: CycleRecoveryAction :: Iterate
204+ if count == ITERATIONS_BEFORE_FALLBACK {
205+ salsa:: CycleRecoveryAction :: Fallback ( ExpressionInference :: cycle_fallback (
206+ expression. scope ( db) ,
207+ ) )
208+ } else {
209+ salsa:: CycleRecoveryAction :: Iterate
210+ }
202211}
203212
204213fn expression_cycle_initial < ' db > (
205214 db : & ' db dyn Db ,
206215 expression : Expression < ' db > ,
207216) -> ExpressionInference < ' db > {
208- ExpressionInference :: cycle_fallback ( expression. scope ( db) )
217+ ExpressionInference :: cycle_initial ( expression. scope ( db) )
209218}
210219
211220/// Infers the type of an `expression` that is guaranteed to be in the same file as the calling query.
@@ -324,7 +333,7 @@ fn unpack_cycle_recover<'db>(
324333}
325334
326335fn unpack_cycle_initial < ' db > ( _db : & ' db dyn Db , _unpack : Unpack < ' db > ) -> UnpackResult < ' db > {
327- UnpackResult :: cycle_fallback ( Type :: Never )
336+ UnpackResult :: cycle_initial ( Type :: Never )
328337}
329338
330339/// Returns the type of the nearest enclosing class for the given scope.
@@ -378,34 +387,61 @@ impl<'db> InferenceRegion<'db> {
378387 }
379388}
380389
390+ #[ derive( Debug , Clone , Copy , Eq , PartialEq , get_size2:: GetSize , salsa:: Update ) ]
391+ enum CycleRecovery < ' db > {
392+ /// An initial-value for fixpoint iteration; all types are `Type::Never`.
393+ Initial ,
394+ /// A divergence-fallback value for fixpoint iteration; all types are `Divergent`.
395+ Divergent ( ScopeId < ' db > ) ,
396+ }
397+
398+ impl < ' db > CycleRecovery < ' db > {
399+ fn merge ( self , other : Option < CycleRecovery < ' db > > ) -> Self {
400+ if let Some ( other) = other {
401+ match ( self , other) {
402+ // It's important here that we keep the scope of `self` if merging two `Divergent`.
403+ ( Self :: Divergent ( scope) , _) | ( _, Self :: Divergent ( scope) ) => Self :: Divergent ( scope) ,
404+ _ => Self :: Initial ,
405+ }
406+ } else {
407+ self
408+ }
409+ }
410+
411+ fn fallback_type ( self ) -> Type < ' db > {
412+ match self {
413+ Self :: Initial => Type :: Never ,
414+ Self :: Divergent ( scope) => Type :: divergent ( scope) ,
415+ }
416+ }
417+ }
418+
381419/// The inferred types for a scope region.
382420#[ derive( Debug , Eq , PartialEq , salsa:: Update , get_size2:: GetSize ) ]
383421pub ( crate ) struct ScopeInference < ' db > {
384422 /// The types of every expression in this region.
385423 expressions : FxHashMap < ExpressionNodeKey , Type < ' db > > ,
386424
387425 /// The extra data that is only present for few inference regions.
388- extra : Option < Box < ScopeInferenceExtra > > ,
426+ extra : Option < Box < ScopeInferenceExtra < ' db > > > ,
389427}
390428
391429#[ derive( Debug , Eq , PartialEq , get_size2:: GetSize , salsa:: Update , Default ) ]
392- struct ScopeInferenceExtra {
393- /// The fallback type for missing expressions/bindings/declarations.
394- ///
395- /// This is used only when constructing a cycle-recovery `TypeInference`.
396- cycle_fallback : bool ,
430+ struct ScopeInferenceExtra < ' db > {
431+ /// Is this a cycle-recovery inference result, and if so, what kind?
432+ cycle_recovery : Option < CycleRecovery < ' db > > ,
397433
398434 /// The diagnostics for this region.
399435 diagnostics : TypeCheckDiagnostics ,
400436}
401437
402438impl < ' db > ScopeInference < ' db > {
403- fn cycle_fallback ( scope : ScopeId < ' db > ) -> Self {
439+ fn cycle_initial ( scope : ScopeId < ' db > ) -> Self {
404440 let _ = scope;
405441
406442 Self {
407443 extra : Some ( Box :: new ( ScopeInferenceExtra {
408- cycle_fallback : true ,
444+ cycle_recovery : Some ( CycleRecovery :: Initial ) ,
409445 ..ScopeInferenceExtra :: default ( )
410446 } ) ) ,
411447 expressions : FxHashMap :: default ( ) ,
@@ -431,14 +467,10 @@ impl<'db> ScopeInference<'db> {
431467 . or_else ( || self . fallback_type ( ) )
432468 }
433469
434- fn is_cycle_callback ( & self ) -> bool {
470+ fn fallback_type ( & self ) -> Option < Type < ' db > > {
435471 self . extra
436472 . as_ref ( )
437- . is_some_and ( |extra| extra. cycle_fallback )
438- }
439-
440- fn fallback_type ( & self ) -> Option < Type < ' db > > {
441- self . is_cycle_callback ( ) . then_some ( Type :: Never )
473+ . and_then ( |extra| extra. cycle_recovery . map ( CycleRecovery :: fallback_type) )
442474 }
443475}
444476
@@ -471,10 +503,8 @@ pub(crate) struct DefinitionInference<'db> {
471503
472504#[ derive( Debug , Eq , PartialEq , get_size2:: GetSize , salsa:: Update , Default ) ]
473505struct DefinitionInferenceExtra < ' db > {
474- /// The fallback type for missing expressions/bindings/declarations.
475- ///
476- /// This is used only when constructing a cycle-recovery `TypeInference`.
477- cycle_fallback : bool ,
506+ /// Is this a cycle-recovery inference result, and if so, what kind?
507+ cycle_recovery : Option < CycleRecovery < ' db > > ,
478508
479509 /// The definitions that are deferred.
480510 deferred : Box < [ Definition < ' db > ] > ,
@@ -487,7 +517,7 @@ struct DefinitionInferenceExtra<'db> {
487517}
488518
489519impl < ' db > DefinitionInference < ' db > {
490- fn cycle_fallback ( scope : ScopeId < ' db > ) -> Self {
520+ fn cycle_initial ( scope : ScopeId < ' db > ) -> Self {
491521 let _ = scope;
492522
493523 Self {
@@ -497,7 +527,7 @@ impl<'db> DefinitionInference<'db> {
497527 #[ cfg( debug_assertions) ]
498528 scope,
499529 extra : Some ( Box :: new ( DefinitionInferenceExtra {
500- cycle_fallback : true ,
530+ cycle_recovery : Some ( CycleRecovery :: Initial ) ,
501531 ..DefinitionInferenceExtra :: default ( )
502532 } ) ) ,
503533 }
@@ -566,14 +596,10 @@ impl<'db> DefinitionInference<'db> {
566596 self . declarations . iter ( ) . map ( |( _, qualifiers) | * qualifiers)
567597 }
568598
569- fn is_cycle_callback ( & self ) -> bool {
599+ fn fallback_type ( & self ) -> Option < Type < ' db > > {
570600 self . extra
571601 . as_ref ( )
572- . is_some_and ( |extra| extra. cycle_fallback )
573- }
574-
575- fn fallback_type ( & self ) -> Option < Type < ' db > > {
576- self . is_cycle_callback ( ) . then_some ( Type :: Never )
602+ . and_then ( |extra| extra. cycle_recovery . map ( CycleRecovery :: fallback_type) )
577603 }
578604
579605 pub ( crate ) fn undecorated_type ( & self ) -> Option < Type < ' db > > {
@@ -605,21 +631,34 @@ struct ExpressionInferenceExtra<'db> {
605631 /// The diagnostics for this region.
606632 diagnostics : TypeCheckDiagnostics ,
607633
608- /// `true` if this region is part of a cycle-recovery `TypeInference`.
609- ///
610- /// Falls back to `Type::Never` if an expression is missing.
611- cycle_fallback : bool ,
634+ /// Is this a cycle recovery inference result, and if so, what kind?
635+ cycle_recovery : Option < CycleRecovery < ' db > > ,
612636
613637 /// `true` if all places in this expression are definitely bound
614638 all_definitely_bound : bool ,
615639}
616640
617641impl < ' db > ExpressionInference < ' db > {
642+ fn cycle_initial ( scope : ScopeId < ' db > ) -> Self {
643+ let _ = scope;
644+ Self {
645+ extra : Some ( Box :: new ( ExpressionInferenceExtra {
646+ cycle_recovery : Some ( CycleRecovery :: Initial ) ,
647+ all_definitely_bound : true ,
648+ ..ExpressionInferenceExtra :: default ( )
649+ } ) ) ,
650+ expressions : FxHashMap :: default ( ) ,
651+
652+ #[ cfg( debug_assertions) ]
653+ scope,
654+ }
655+ }
656+
618657 fn cycle_fallback ( scope : ScopeId < ' db > ) -> Self {
619658 let _ = scope;
620659 Self {
621660 extra : Some ( Box :: new ( ExpressionInferenceExtra {
622- cycle_fallback : true ,
661+ cycle_recovery : Some ( CycleRecovery :: Divergent ( scope ) ) ,
623662 all_definitely_bound : true ,
624663 ..ExpressionInferenceExtra :: default ( )
625664 } ) ) ,
@@ -645,14 +684,10 @@ impl<'db> ExpressionInference<'db> {
645684 . unwrap_or_else ( Type :: unknown)
646685 }
647686
648- fn is_cycle_callback ( & self ) -> bool {
687+ fn fallback_type ( & self ) -> Option < Type < ' db > > {
649688 self . extra
650689 . as_ref ( )
651- . is_some_and ( |extra| extra. cycle_fallback )
652- }
653-
654- fn fallback_type ( & self ) -> Option < Type < ' db > > {
655- self . is_cycle_callback ( ) . then_some ( Type :: Never )
690+ . and_then ( |extra| extra. cycle_recovery . map ( CycleRecovery :: fallback_type) )
656691 }
657692
658693 /// Returns true if all places in this expression are definitely bound.
0 commit comments