11use crate :: check:: FnCtxt ;
2+ use rustc_data_structures:: {
3+ fx:: FxHashMap , graph:: vec_graph:: VecGraph , graph:: WithSuccessors , stable_set:: FxHashSet ,
4+ } ;
25use rustc_infer:: infer:: type_variable:: Diverging ;
36use rustc_middle:: ty:: { self , Ty } ;
47
@@ -8,22 +11,30 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
811 pub ( super ) fn type_inference_fallback ( & self ) -> bool {
912 // All type checking constraints were added, try to fallback unsolved variables.
1013 self . select_obligations_where_possible ( false , |_| { } ) ;
11- let mut fallback_has_occurred = false ;
1214
15+ // Check if we have any unsolved varibales. If not, no need for fallback.
16+ let unsolved_variables = self . unsolved_variables ( ) ;
17+ if unsolved_variables. is_empty ( ) {
18+ return false ;
19+ }
20+
21+ let diverging_fallback = self . calculate_diverging_fallback ( & unsolved_variables) ;
22+
23+ let mut fallback_has_occurred = false ;
1324 // We do fallback in two passes, to try to generate
1425 // better error messages.
1526 // The first time, we do *not* replace opaque types.
16- for ty in & self . unsolved_variables ( ) {
27+ for ty in unsolved_variables {
1728 debug ! ( "unsolved_variable = {:?}" , ty) ;
18- fallback_has_occurred |= self . fallback_if_possible ( ty) ;
29+ fallback_has_occurred |= self . fallback_if_possible ( ty, & diverging_fallback ) ;
1930 }
2031
21- // We now see if we can make progress. This might
22- // cause us to unify inference variables for opaque types,
23- // since we may have unified some other type variables
24- // during the first phase of fallback.
25- // This means that we only replace inference variables with their underlying
26- // opaque types as a last resort.
32+ // We now see if we can make progress. This might cause us to
33+ // unify inference variables for opaque types, since we may
34+ // have unified some other type variables during the first
35+ // phase of fallback. This means that we only replace
36+ // inference variables with their underlying opaque types as a
37+ // last resort.
2738 //
2839 // In code like this:
2940 //
@@ -62,36 +73,44 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
6273 //
6374 // - Unconstrained floats are replaced with with `f64`.
6475 //
65- // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
66- // is enabled. Otherwise, they are replaced with `()`.
76+ // - Non-numerics may get replaced with `()` or `!`, depending on
77+ // how they were categorized by `calculate_diverging_fallback`
78+ // (and the setting of `#![feature(never_type_fallback)]`).
79+ //
80+ // Fallback becomes very dubious if we have encountered
81+ // type-checking errors. In that case, fallback to Error.
6782 //
68- // Fallback becomes very dubious if we have encountered type-checking errors.
69- // In that case, fallback to Error.
7083 // The return value indicates whether fallback has occurred.
71- fn fallback_if_possible ( & self , ty : Ty < ' tcx > ) -> bool {
84+ fn fallback_if_possible (
85+ & self ,
86+ ty : Ty < ' tcx > ,
87+ diverging_fallback : & FxHashMap < Ty < ' tcx > , Ty < ' tcx > > ,
88+ ) -> bool {
7289 // Careful: we do NOT shallow-resolve `ty`. We know that `ty`
73- // is an unsolved variable, and we determine its fallback based
74- // solely on how it was created, not what other type variables
75- // it may have been unified with since then.
90+ // is an unsolved variable, and we determine its fallback
91+ // based solely on how it was created, not what other type
92+ // variables it may have been unified with since then.
7693 //
77- // The reason this matters is that other attempts at fallback may
78- // (in principle) conflict with this fallback, and we wish to generate
79- // a type error in that case. (However, this actually isn't true right now,
80- // because we're only using the builtin fallback rules. This would be
81- // true if we were using user-supplied fallbacks. But it's still useful
82- // to write the code to detect bugs.)
94+ // The reason this matters is that other attempts at fallback
95+ // may (in principle) conflict with this fallback, and we wish
96+ // to generate a type error in that case. (However, this
97+ // actually isn't true right now, because we're only using the
98+ // builtin fallback rules. This would be true if we were using
99+ // user-supplied fallbacks. But it's still useful to write the
100+ // code to detect bugs.)
83101 //
84- // (Note though that if we have a general type variable `?T` that is then unified
85- // with an integer type variable `?I` that ultimately never gets
86- // resolved to a special integral type, `?T` is not considered unsolved,
87- // but `?I` is. The same is true for float variables.)
102+ // (Note though that if we have a general type variable `?T`
103+ // that is then unified with an integer type variable `?I`
104+ // that ultimately never gets resolved to a special integral
105+ // type, `?T` is not considered unsolved, but `?I` is. The
106+ // same is true for float variables.)
88107 let fallback = match ty. kind ( ) {
89108 _ if self . is_tainted_by_errors ( ) => self . tcx . ty_error ( ) ,
90109 ty:: Infer ( ty:: IntVar ( _) ) => self . tcx . types . i32 ,
91110 ty:: Infer ( ty:: FloatVar ( _) ) => self . tcx . types . f64 ,
92- _ => match self . type_var_diverges ( ty) {
93- Diverging :: Diverges => self . tcx . mk_diverging_default ( ) ,
94- Diverging :: NotDiverging => return false ,
111+ _ => match diverging_fallback . get ( & ty) {
112+ Some ( & fallback_ty ) => fallback_ty ,
113+ None => return false ,
95114 } ,
96115 } ;
97116 debug ! ( "fallback_if_possible(ty={:?}): defaulting to `{:?}`" , ty, fallback) ;
@@ -105,11 +124,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
105124 true
106125 }
107126
108- /// Second round of fallback: Unconstrained type variables
109- /// created from the instantiation of an opaque
110- /// type fall back to the opaque type itself. This is a
111- /// somewhat incomplete attempt to manage "identity passthrough"
112- /// for `impl Trait` types.
127+ /// Second round of fallback: Unconstrained type variables created
128+ /// from the instantiation of an opaque type fall back to the
129+ /// opaque type itself. This is a somewhat incomplete attempt to
130+ /// manage "identity passthrough" for `impl Trait` types.
113131 ///
114132 /// For example, in this code:
115133 ///
@@ -158,4 +176,195 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
158176 return false ;
159177 }
160178 }
179+
180+ /// The "diverging fallback" system is rather complicated. This is
181+ /// a result of our need to balance 'do the right thing' with
182+ /// backwards compatibility.
183+ ///
184+ /// "Diverging" type variables are variables created when we
185+ /// coerce a `!` type into an unbound type variable `?X`. If they
186+ /// never wind up being constrained, the "right and natural" thing
187+ /// is that `?X` should "fallback" to `!`. This means that e.g. an
188+ /// expression like `Some(return)` will ultimately wind up with a
189+ /// type like `Option<!>` (presuming it is not assigned or
190+ /// constrained to have some other type).
191+ ///
192+ /// However, the fallback used to be `()` (before the `!` type was
193+ /// added). Moreover, there are cases where the `!` type 'leaks
194+ /// out' from dead code into type variables that affect live
195+ /// code. The most common case is something like this:
196+ ///
197+ /// ```rust
198+ /// match foo() {
199+ /// 22 => Default::default(), // call this type `?D`
200+ /// _ => return, // return has type `!`
201+ /// } // call the type of this match `?M`
202+ /// ```
203+ ///
204+ /// Here, coercing the type `!` into `?M` will create a diverging
205+ /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
206+ /// ?M`. If `?M` winds up unconstrained, then `?X` will
207+ /// fallback. If it falls back to `!`, then all the type variables
208+ /// will wind up equal to `!` -- this includes the type `?D`
209+ /// (since `!` doesn't implement `Default`, we wind up a "trait
210+ /// not implemented" error in code like this). But since the
211+ /// original fallback was `()`, this code used to compile with `?D
212+ /// = ()`. This is somewhat surprising, since `Default::default()`
213+ /// on its own would give an error because the types are
214+ /// insufficiently constrained.
215+ ///
216+ /// Our solution to this dilemma is to modify diverging variables
217+ /// so that they can *either* fallback to `!` (the default) or to
218+ /// `()` (the backwards compatibility case). We decide which
219+ /// fallback to use based on whether there is a coercion pattern
220+ /// like this:
221+ ///
222+ /// ```
223+ /// ?Diverging -> ?V
224+ /// ?NonDiverging -> ?V
225+ /// ?V != ?NonDiverging
226+ /// ```
227+ ///
228+ /// Here `?Diverging` represents some diverging type variable and
229+ /// `?NonDiverging` represents some non-diverging type
230+ /// variable. `?V` can be any type variable (diverging or not), so
231+ /// long as it is not equal to `?NonDiverging`.
232+ ///
233+ /// Intuitively, what we are looking for is a case where a
234+ /// "non-diverging" type variable (like `?M` in our example above)
235+ /// is coerced *into* some variable `?V` that would otherwise
236+ /// fallback to `!`. In that case, we make `?V` fallback to `!`,
237+ /// along with anything that would flow into `?V`.
238+ ///
239+ /// The algorithm we use:
240+ /// * Identify all variables that are coerced *into* by a
241+ /// diverging variable. Do this by iterating over each
242+ /// diverging, unsolved variable and finding all variables
243+ /// reachable from there. Call that set `D`.
244+ /// * Walk over all unsolved, non-diverging variables, and find
245+ /// any variable that has an edge into `D`.
246+ fn calculate_diverging_fallback (
247+ & self ,
248+ unsolved_variables : & [ Ty < ' tcx > ] ,
249+ ) -> FxHashMap < Ty < ' tcx > , Ty < ' tcx > > {
250+ debug ! ( "calculate_diverging_fallback({:?})" , unsolved_variables) ;
251+
252+ // Construct a coercion graph where an edge `A -> B` indicates
253+ // a type variable is that is coerced
254+ let coercion_graph = self . create_coercion_graph ( ) ;
255+
256+ // Extract the unsolved type inference variable vids; note that some
257+ // unsolved variables are integer/float variables and are excluded.
258+ let unsolved_vids: Vec < _ > =
259+ unsolved_variables. iter ( ) . filter_map ( |ty| ty. ty_vid ( ) ) . collect ( ) ;
260+
261+ // Find all type variables that are reachable from a diverging
262+ // type variable. These will typically default to `!`, unless
263+ // we find later that they are *also* reachable from some
264+ // other type variable outside this set.
265+ let mut roots_reachable_from_diverging = FxHashSet :: default ( ) ;
266+ let mut diverging_vids = vec ! [ ] ;
267+ let mut non_diverging_vids = vec ! [ ] ;
268+ for & unsolved_vid in & unsolved_vids {
269+ debug ! (
270+ "calculate_diverging_fallback: unsolved_vid={:?} diverges={:?}" ,
271+ unsolved_vid,
272+ self . infcx. ty_vid_diverges( unsolved_vid)
273+ ) ;
274+ match self . infcx . ty_vid_diverges ( unsolved_vid) {
275+ Diverging :: Diverges => {
276+ diverging_vids. push ( unsolved_vid) ;
277+ let root_vid = self . infcx . root_var ( unsolved_vid) ;
278+ debug ! (
279+ "calculate_diverging_fallback: root_vid={:?} reaches {:?}" ,
280+ root_vid,
281+ coercion_graph. depth_first_search( root_vid) . collect:: <Vec <_>>( )
282+ ) ;
283+ roots_reachable_from_diverging
284+ . extend ( coercion_graph. depth_first_search ( root_vid) ) ;
285+ }
286+ Diverging :: NotDiverging => {
287+ non_diverging_vids. push ( unsolved_vid) ;
288+ }
289+ }
290+ }
291+ debug ! (
292+ "calculate_diverging_fallback: roots_reachable_from_diverging={:?}" ,
293+ roots_reachable_from_diverging,
294+ ) ;
295+
296+ // Find all type variables N0 that are not reachable from a
297+ // diverging variable, and then compute the set reachable from
298+ // N0, which we call N. These are the *non-diverging* type
299+ // variables. (Note that this set consists of "root variables".)
300+ let mut roots_reachable_from_non_diverging = FxHashSet :: default ( ) ;
301+ for & non_diverging_vid in & non_diverging_vids {
302+ let root_vid = self . infcx . root_var ( non_diverging_vid) ;
303+ if roots_reachable_from_diverging. contains ( & root_vid) {
304+ continue ;
305+ }
306+ roots_reachable_from_non_diverging. extend ( coercion_graph. depth_first_search ( root_vid) ) ;
307+ }
308+ debug ! (
309+ "calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}" ,
310+ roots_reachable_from_non_diverging,
311+ ) ;
312+
313+ // For each diverging variable, figure out whether it can
314+ // reach a member of N. If so, it falls back to `()`. Else
315+ // `!`.
316+ let mut diverging_fallback = FxHashMap :: default ( ) ;
317+ for & diverging_vid in & diverging_vids {
318+ let diverging_ty = self . tcx . mk_ty_var ( diverging_vid) ;
319+ let root_vid = self . infcx . root_var ( diverging_vid) ;
320+ let can_reach_non_diverging = coercion_graph
321+ . depth_first_search ( root_vid)
322+ . any ( |n| roots_reachable_from_non_diverging. contains ( & n) ) ;
323+ if can_reach_non_diverging {
324+ debug ! ( "fallback to (): {:?}" , diverging_vid) ;
325+ diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
326+ } else {
327+ debug ! ( "fallback to !: {:?}" , diverging_vid) ;
328+ diverging_fallback. insert ( diverging_ty, self . tcx . mk_diverging_default ( ) ) ;
329+ }
330+ }
331+
332+ diverging_fallback
333+ }
334+
335+ /// Returns a graph whose nodes are (unresolved) inference variables and where
336+ /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
337+ fn create_coercion_graph ( & self ) -> VecGraph < ty:: TyVid > {
338+ let pending_obligations = self . fulfillment_cx . borrow_mut ( ) . pending_obligations ( ) ;
339+ debug ! ( "create_coercion_graph: pending_obligations={:?}" , pending_obligations) ;
340+ let coercion_edges: Vec < ( ty:: TyVid , ty:: TyVid ) > = pending_obligations
341+ . into_iter ( )
342+ . filter_map ( |obligation| {
343+ // The predicates we are looking for look like `Coerce(?A -> ?B)`.
344+ // They will have no bound variables.
345+ obligation. predicate . kind ( ) . no_bound_vars ( )
346+ } )
347+ . filter_map ( |atom| {
348+ if let ty:: PredicateKind :: Coerce ( ty:: CoercePredicate { a, b } ) = atom {
349+ let a_vid = self . root_vid ( a) ?;
350+ let b_vid = self . root_vid ( b) ?;
351+ Some ( ( a_vid, b_vid) )
352+ } else {
353+ return None ;
354+ } ;
355+
356+ let a_vid = self . root_vid ( a) ?;
357+ let b_vid = self . root_vid ( b) ?;
358+ Some ( ( a_vid, b_vid) )
359+ } )
360+ . collect ( ) ;
361+ debug ! ( "create_coercion_graph: coercion_edges={:?}" , coercion_edges) ;
362+ let num_ty_vars = self . infcx . num_ty_vars ( ) ;
363+ VecGraph :: new ( num_ty_vars, coercion_edges)
364+ }
365+
366+ /// If `ty` is an unresolved type variable, returns its root vid.
367+ fn root_vid ( & self , ty : Ty < ' tcx > ) -> Option < ty:: TyVid > {
368+ Some ( self . infcx . root_var ( self . infcx . shallow_resolve ( ty) . ty_vid ( ) ?) )
369+ }
161370}
0 commit comments