hir_ty/infer/fallback.rs
1//! Fallback of infer vars to `!` and `i32`/`f64`.
2
3use petgraph::{
4 Graph,
5 visit::{Dfs, Walker},
6};
7use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
8use rustc_type_ir::{
9 TyVid,
10 inherent::{IntoKind, Ty as _},
11};
12use tracing::debug;
13
14use crate::{
15 infer::InferenceContext,
16 next_solver::{CoercePredicate, PredicateKind, SubtypePredicate, Ty, TyKind},
17};
18
19#[derive(Copy, Clone)]
20pub(crate) enum DivergingFallbackBehavior {
21 /// Always fallback to `()` (aka "always spontaneous decay")
22 ToUnit,
23 /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
24 ContextDependent,
25 /// Always fallback to `!` (which should be equivalent to never falling back + not making
26 /// never-to-any coercions unless necessary)
27 ToNever,
28}
29
30impl<'db> InferenceContext<'db> {
31 pub(super) fn type_inference_fallback(&mut self) {
32 debug!(
33 "type-inference-fallback start obligations: {:#?}",
34 self.table.fulfillment_cx.pending_obligations()
35 );
36
37 // All type checking constraints were added, try to fallback unsolved variables.
38 self.table.select_obligations_where_possible();
39
40 debug!(
41 "type-inference-fallback post selection obligations: {:#?}",
42 self.table.fulfillment_cx.pending_obligations()
43 );
44
45 let fallback_occurred = self.fallback_types();
46
47 if !fallback_occurred {
48 return;
49 }
50
51 // We now see if we can make progress. This might cause us to
52 // unify inference variables for opaque types, since we may
53 // have unified some other type variables during the first
54 // phase of fallback. This means that we only replace
55 // inference variables with their underlying opaque types as a
56 // last resort.
57 //
58 // In code like this:
59 //
60 // ```rust
61 // type MyType = impl Copy;
62 // fn produce() -> MyType { true }
63 // fn bad_produce() -> MyType { panic!() }
64 // ```
65 //
66 // we want to unify the opaque inference variable in `bad_produce`
67 // with the diverging fallback for `panic!` (e.g. `()` or `!`).
68 // This will produce a nice error message about conflicting concrete
69 // types for `MyType`.
70 //
71 // If we had tried to fallback the opaque inference variable to `MyType`,
72 // we will generate a confusing type-check error that does not explicitly
73 // refer to opaque types.
74 self.table.select_obligations_where_possible();
75 }
76
77 fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
78 if self.edition.at_least_2024() {
79 return DivergingFallbackBehavior::ToNever;
80 }
81
82 if self.features.never_type_fallback {
83 return DivergingFallbackBehavior::ContextDependent;
84 }
85
86 DivergingFallbackBehavior::ToUnit
87 }
88
89 fn fallback_types(&mut self) -> bool {
90 // Check if we have any unresolved variables. If not, no need for fallback.
91 let unresolved_variables = self.table.infer_ctxt.unresolved_variables();
92
93 if unresolved_variables.is_empty() {
94 return false;
95 }
96
97 let diverging_fallback_behavior = self.diverging_fallback_behavior();
98
99 let diverging_fallback =
100 self.calculate_diverging_fallback(&unresolved_variables, diverging_fallback_behavior);
101
102 // We do fallback in two passes, to try to generate
103 // better error messages.
104 // The first time, we do *not* replace opaque types.
105 let mut fallback_occurred = false;
106 for ty in unresolved_variables {
107 debug!("unsolved_variable = {:?}", ty);
108 fallback_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
109 }
110
111 fallback_occurred
112 }
113
114 // Tries to apply a fallback to `ty` if it is an unsolved variable.
115 //
116 // - Unconstrained ints are replaced with `i32`.
117 //
118 // - Unconstrained floats are replaced with `f64`.
119 //
120 // - Non-numerics may get replaced with `()` or `!`, depending on
121 // how they were categorized by `calculate_diverging_fallback`
122 // (and the setting of `#![feature(never_type_fallback)]`).
123 //
124 // Fallback becomes very dubious if we have encountered
125 // type-checking errors. In that case, fallback to Error.
126 //
127 // Sets `FnCtxt::fallback_has_occurred` if fallback is performed
128 // during this call.
129 fn fallback_if_possible(
130 &mut self,
131 ty: Ty<'db>,
132 diverging_fallback: &FxHashMap<Ty<'db>, Ty<'db>>,
133 ) -> bool {
134 // Careful: we do NOT shallow-resolve `ty`. We know that `ty`
135 // is an unsolved variable, and we determine its fallback
136 // based solely on how it was created, not what other type
137 // variables it may have been unified with since then.
138 //
139 // The reason this matters is that other attempts at fallback
140 // may (in principle) conflict with this fallback, and we wish
141 // to generate a type error in that case. (However, this
142 // actually isn't true right now, because we're only using the
143 // builtin fallback rules. This would be true if we were using
144 // user-supplied fallbacks. But it's still useful to write the
145 // code to detect bugs.)
146 //
147 // (Note though that if we have a general type variable `?T`
148 // that is then unified with an integer type variable `?I`
149 // that ultimately never gets resolved to a special integral
150 // type, `?T` is not considered unsolved, but `?I` is. The
151 // same is true for float variables.)
152 let fallback = match ty.kind() {
153 TyKind::Infer(rustc_type_ir::IntVar(_)) => self.types.types.i32,
154 TyKind::Infer(rustc_type_ir::FloatVar(_)) => self.types.types.f64,
155 _ => match diverging_fallback.get(&ty) {
156 Some(&fallback_ty) => fallback_ty,
157 None => return false,
158 },
159 };
160 debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
161
162 _ = self.demand_eqtype_fixme_no_diag(ty, fallback);
163 true
164 }
165
166 /// The "diverging fallback" system is rather complicated. This is
167 /// a result of our need to balance 'do the right thing' with
168 /// backwards compatibility.
169 ///
170 /// "Diverging" type variables are variables created when we
171 /// coerce a `!` type into an unbound type variable `?X`. If they
172 /// never wind up being constrained, the "right and natural" thing
173 /// is that `?X` should "fallback" to `!`. This means that e.g. an
174 /// expression like `Some(return)` will ultimately wind up with a
175 /// type like `Option<!>` (presuming it is not assigned or
176 /// constrained to have some other type).
177 ///
178 /// However, the fallback used to be `()` (before the `!` type was
179 /// added). Moreover, there are cases where the `!` type 'leaks
180 /// out' from dead code into type variables that affect live
181 /// code. The most common case is something like this:
182 ///
183 /// ```rust
184 /// # fn foo() -> i32 { 4 }
185 /// match foo() {
186 /// 22 => Default::default(), // call this type `?D`
187 /// _ => return, // return has type `!`
188 /// } // call the type of this match `?M`
189 /// ```
190 ///
191 /// Here, coercing the type `!` into `?M` will create a diverging
192 /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
193 /// ?M`. If `?M` winds up unconstrained, then `?X` will
194 /// fallback. If it falls back to `!`, then all the type variables
195 /// will wind up equal to `!` -- this includes the type `?D`
196 /// (since `!` doesn't implement `Default`, we wind up a "trait
197 /// not implemented" error in code like this). But since the
198 /// original fallback was `()`, this code used to compile with `?D
199 /// = ()`. This is somewhat surprising, since `Default::default()`
200 /// on its own would give an error because the types are
201 /// insufficiently constrained.
202 ///
203 /// Our solution to this dilemma is to modify diverging variables
204 /// so that they can *either* fallback to `!` (the default) or to
205 /// `()` (the backwards compatibility case). We decide which
206 /// fallback to use based on whether there is a coercion pattern
207 /// like this:
208 ///
209 /// ```ignore (not-rust)
210 /// ?Diverging -> ?V
211 /// ?NonDiverging -> ?V
212 /// ?V != ?NonDiverging
213 /// ```
214 ///
215 /// Here `?Diverging` represents some diverging type variable and
216 /// `?NonDiverging` represents some non-diverging type
217 /// variable. `?V` can be any type variable (diverging or not), so
218 /// long as it is not equal to `?NonDiverging`.
219 ///
220 /// Intuitively, what we are looking for is a case where a
221 /// "non-diverging" type variable (like `?M` in our example above)
222 /// is coerced *into* some variable `?V` that would otherwise
223 /// fallback to `!`. In that case, we make `?V` fallback to `!`,
224 /// along with anything that would flow into `?V`.
225 ///
226 /// The algorithm we use:
227 /// * Identify all variables that are coerced *into* by a
228 /// diverging variable. Do this by iterating over each
229 /// diverging, unsolved variable and finding all variables
230 /// reachable from there. Call that set `D`.
231 /// * Walk over all unsolved, non-diverging variables, and find
232 /// any variable that has an edge into `D`.
233 fn calculate_diverging_fallback(
234 &self,
235 unresolved_variables: &[Ty<'db>],
236 behavior: DivergingFallbackBehavior,
237 ) -> FxHashMap<Ty<'db>, Ty<'db>> {
238 debug!("calculate_diverging_fallback({:?})", unresolved_variables);
239
240 // Construct a coercion graph where an edge `A -> B` indicates
241 // a type variable is that is coerced
242 let coercion_graph = self.create_coercion_graph();
243
244 // Extract the unsolved type inference variable vids; note that some
245 // unsolved variables are integer/float variables and are excluded.
246 let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid());
247
248 // Compute the diverging root vids D -- that is, the root vid of
249 // those type variables that (a) are the target of a coercion from
250 // a `!` type and (b) have not yet been solved.
251 //
252 // These variables are the ones that are targets for fallback to
253 // either `!` or `()`.
254 let diverging_roots: FxHashSet<TyVid> = self
255 .table
256 .diverging_type_vars
257 .iter()
258 .map(|&ty| self.shallow_resolve(ty))
259 .filter_map(|ty| ty.ty_vid())
260 .map(|vid| self.table.infer_ctxt.root_var(vid))
261 .collect();
262 debug!(
263 "calculate_diverging_fallback: diverging_type_vars={:?}",
264 self.table.diverging_type_vars
265 );
266 debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
267
268 // Find all type variables that are reachable from a diverging
269 // type variable. These will typically default to `!`, unless
270 // we find later that they are *also* reachable from some
271 // other type variable outside this set.
272 let mut roots_reachable_from_diverging = Dfs::empty(&coercion_graph);
273 let mut diverging_vids = vec![];
274 let mut non_diverging_vids = vec![];
275 for unsolved_vid in unsolved_vids {
276 let root_vid = self.table.infer_ctxt.root_var(unsolved_vid);
277 debug!(
278 "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
279 unsolved_vid,
280 root_vid,
281 diverging_roots.contains(&root_vid),
282 );
283 if diverging_roots.contains(&root_vid) {
284 diverging_vids.push(unsolved_vid);
285 roots_reachable_from_diverging.move_to(root_vid.as_u32().into());
286
287 // drain the iterator to visit all nodes reachable from this node
288 while roots_reachable_from_diverging.next(&coercion_graph).is_some() {}
289 } else {
290 non_diverging_vids.push(unsolved_vid);
291 }
292 }
293
294 debug!(
295 "calculate_diverging_fallback: roots_reachable_from_diverging={:?}",
296 roots_reachable_from_diverging,
297 );
298
299 // Find all type variables N0 that are not reachable from a
300 // diverging variable, and then compute the set reachable from
301 // N0, which we call N. These are the *non-diverging* type
302 // variables. (Note that this set consists of "root variables".)
303 let mut roots_reachable_from_non_diverging = Dfs::empty(&coercion_graph);
304 for &non_diverging_vid in &non_diverging_vids {
305 let root_vid = self.table.infer_ctxt.root_var(non_diverging_vid);
306 if roots_reachable_from_diverging.discovered.contains(root_vid.as_usize()) {
307 continue;
308 }
309 roots_reachable_from_non_diverging.move_to(root_vid.as_u32().into());
310 while roots_reachable_from_non_diverging.next(&coercion_graph).is_some() {}
311 }
312 debug!(
313 "calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}",
314 roots_reachable_from_non_diverging,
315 );
316
317 debug!("obligations: {:#?}", self.table.fulfillment_cx.pending_obligations());
318
319 // For each diverging variable, figure out whether it can
320 // reach a member of N. If so, it falls back to `()`. Else
321 // `!`.
322 let mut diverging_fallback =
323 FxHashMap::with_capacity_and_hasher(diverging_vids.len(), FxBuildHasher);
324
325 for &diverging_vid in &diverging_vids {
326 let diverging_ty = Ty::new_var(self.interner(), diverging_vid);
327 let root_vid = self.table.infer_ctxt.root_var(diverging_vid);
328 let can_reach_non_diverging = Dfs::new(&coercion_graph, root_vid.as_u32().into())
329 .iter(&coercion_graph)
330 .any(|n| roots_reachable_from_non_diverging.discovered.contains(n.index()));
331
332 let mut fallback_to = |ty| {
333 diverging_fallback.insert(diverging_ty, ty);
334 };
335
336 match behavior {
337 DivergingFallbackBehavior::ToUnit => {
338 debug!("fallback to () - legacy: {:?}", diverging_vid);
339 fallback_to(self.types.types.unit);
340 }
341 DivergingFallbackBehavior::ContextDependent => {
342 // FIXME: rustc does the following, but given this is only relevant when the unstable
343 // `never_type_fallback` feature is active, I chose to not port this.
344 // if found_infer_var_info.self_in_trait && found_infer_var_info.output {
345 // // This case falls back to () to ensure that the code pattern in
346 // // tests/ui/never_type/fallback-closure-ret.rs continues to
347 // // compile when never_type_fallback is enabled.
348 // //
349 // // This rule is not readily explainable from first principles,
350 // // but is rather intended as a patchwork fix to ensure code
351 // // which compiles before the stabilization of never type
352 // // fallback continues to work.
353 // //
354 // // Typically this pattern is encountered in a function taking a
355 // // closure as a parameter, where the return type of that closure
356 // // (checked by `relationship.output`) is expected to implement
357 // // some trait (checked by `relationship.self_in_trait`). This
358 // // can come up in non-closure cases too, so we do not limit this
359 // // rule to specifically `FnOnce`.
360 // //
361 // // When the closure's body is something like `panic!()`, the
362 // // return type would normally be inferred to `!`. However, it
363 // // needs to fall back to `()` in order to still compile, as the
364 // // trait is specifically implemented for `()` but not `!`.
365 // //
366 // // For details on the requirements for these relationships to be
367 // // set, see the relationship finding module in
368 // // compiler/rustc_trait_selection/src/traits/relationships.rs.
369 // debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
370 // fallback_to(self.types.types.unit);
371 // }
372 if can_reach_non_diverging {
373 debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
374 fallback_to(self.types.types.unit);
375 } else {
376 debug!("fallback to ! - all diverging: {:?}", diverging_vid);
377 fallback_to(self.types.types.never);
378 }
379 }
380 DivergingFallbackBehavior::ToNever => {
381 debug!(
382 "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}",
383 diverging_vid
384 );
385 fallback_to(self.types.types.never);
386 }
387 }
388 }
389
390 diverging_fallback
391 }
392
393 /// Returns a graph whose nodes are (unresolved) inference variables and where
394 /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
395 fn create_coercion_graph(&self) -> Graph<(), ()> {
396 let pending_obligations = self.table.fulfillment_cx.pending_obligations();
397 let pending_obligations_len = pending_obligations.len();
398 debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
399 let coercion_edges = pending_obligations
400 .into_iter()
401 .filter_map(|obligation| {
402 // The predicates we are looking for look like `Coerce(?A -> ?B)`.
403 // They will have no bound variables.
404 obligation.predicate.kind().no_bound_vars()
405 })
406 .filter_map(|atom| {
407 // We consider both subtyping and coercion to imply 'flow' from
408 // some position in the code `a` to a different position `b`.
409 // This is then used to determine which variables interact with
410 // live code, and as such must fall back to `()` to preserve
411 // soundness.
412 //
413 // In practice currently the two ways that this happens is
414 // coercion and subtyping.
415 let (a, b) = match atom {
416 PredicateKind::Coerce(CoercePredicate { a, b }) => (a, b),
417 PredicateKind::Subtype(SubtypePredicate { a_is_expected: _, a, b }) => (a, b),
418 _ => return None,
419 };
420
421 let a_vid = self.root_vid(a)?;
422 let b_vid = self.root_vid(b)?;
423 Some((a_vid.as_u32(), b_vid.as_u32()))
424 });
425 let num_ty_vars = self.table.infer_ctxt.num_ty_vars();
426 let mut graph = Graph::with_capacity(num_ty_vars, pending_obligations_len);
427 for _ in 0..num_ty_vars {
428 graph.add_node(());
429 }
430 graph.extend_with_edges(coercion_edges);
431 graph
432 }
433
434 /// If `ty` is an unresolved type variable, returns its root vid.
435 fn root_vid(&self, ty: Ty<'db>) -> Option<TyVid> {
436 Some(self.table.infer_ctxt.root_var(self.shallow_resolve(ty).ty_vid()?))
437 }
438}