1
1
use crate :: check:: FnCtxt ;
2
+ use rustc_data_structures:: {
3
+ fx:: FxHashMap , graph:: vec_graph:: VecGraph , graph:: WithSuccessors , stable_set:: FxHashSet ,
4
+ } ;
2
5
use rustc_infer:: infer:: type_variable:: Diverging ;
3
6
use rustc_middle:: ty:: { self , Ty } ;
4
7
@@ -8,22 +11,30 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
8
11
pub ( super ) fn type_inference_fallback ( & self ) -> bool {
9
12
// All type checking constraints were added, try to fallback unsolved variables.
10
13
self . select_obligations_where_possible ( false , |_| { } ) ;
11
- let mut fallback_has_occurred = false ;
12
14
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 ;
13
24
// We do fallback in two passes, to try to generate
14
25
// better error messages.
15
26
// The first time, we do *not* replace opaque types.
16
- for ty in & self . unsolved_variables ( ) {
27
+ for ty in unsolved_variables {
17
28
debug ! ( "unsolved_variable = {:?}" , ty) ;
18
- fallback_has_occurred |= self . fallback_if_possible ( ty) ;
29
+ fallback_has_occurred |= self . fallback_if_possible ( ty, & diverging_fallback ) ;
19
30
}
20
31
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.
27
38
//
28
39
// In code like this:
29
40
//
@@ -62,36 +73,44 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
62
73
//
63
74
// - Unconstrained floats are replaced with with `f64`.
64
75
//
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.
67
82
//
68
- // Fallback becomes very dubious if we have encountered type-checking errors.
69
- // In that case, fallback to Error.
70
83
// 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 {
72
89
// 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.
76
93
//
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.)
83
101
//
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.)
88
107
let fallback = match ty. kind ( ) {
89
108
_ if self . is_tainted_by_errors ( ) => self . tcx . ty_error ( ) ,
90
109
ty:: Infer ( ty:: IntVar ( _) ) => self . tcx . types . i32 ,
91
110
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 ,
95
114
} ,
96
115
} ;
97
116
debug ! ( "fallback_if_possible(ty={:?}): defaulting to `{:?}`" , ty, fallback) ;
@@ -105,11 +124,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
105
124
true
106
125
}
107
126
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.
113
131
///
114
132
/// For example, in this code:
115
133
///
@@ -158,4 +176,195 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
158
176
return false ;
159
177
}
160
178
}
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
+ }
161
370
}
0 commit comments