]>
Commit | Line | Data |
---|---|---|
f2b60f7d | 1 | use crate::infer::InferCtxt; |
353b0b11 | 2 | use crate::traits::{ObligationCause, ObligationCtxt}; |
487cf647 | 3 | use rustc_data_structures::fx::FxIndexSet; |
353b0b11 | 4 | use rustc_infer::infer::resolve::OpportunisticRegionResolver; |
49aad941 FG |
5 | use rustc_infer::infer::InferOk; |
6 | use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; | |
353b0b11 | 7 | use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; |
9ffffee4 | 8 | use rustc_span::def_id::LocalDefId; |
8faf50e0 | 9 | |
ba9703b0 | 10 | pub use rustc_middle::traits::query::OutlivesBound; |
8faf50e0 | 11 | |
f2b60f7d FG |
12 | type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; |
13 | pub trait InferCtxtExt<'a, 'tcx> { | |
ba9703b0 XL |
14 | fn implied_outlives_bounds( |
15 | &self, | |
16 | param_env: ty::ParamEnv<'tcx>, | |
9ffffee4 | 17 | body_id: LocalDefId, |
ba9703b0 | 18 | ty: Ty<'tcx>, |
ba9703b0 | 19 | ) -> Vec<OutlivesBound<'tcx>>; |
f2b60f7d FG |
20 | |
21 | fn implied_bounds_tys( | |
22 | &'a self, | |
23 | param_env: ty::ParamEnv<'tcx>, | |
9ffffee4 | 24 | body_id: LocalDefId, |
487cf647 | 25 | tys: FxIndexSet<Ty<'tcx>>, |
f2b60f7d | 26 | ) -> Bounds<'a, 'tcx>; |
ba9703b0 XL |
27 | } |
28 | ||
2b03887a | 29 | impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { |
8faf50e0 | 30 | /// Implied bounds are region relationships that we deduce |
9fa01778 | 31 | /// automatically. The idea is that (e.g.) a caller must check that a |
8faf50e0 XL |
32 | /// function's argument types are well-formed immediately before |
33 | /// calling that fn, and hence the *callee* can assume that its | |
34 | /// argument types are well-formed. This may imply certain relationships | |
35 | /// between generic parameters. For example: | |
04454e1e | 36 | /// ``` |
9c376795 | 37 | /// fn foo<T>(x: &T) {} |
04454e1e | 38 | /// ``` |
8faf50e0 XL |
39 | /// can only be called with a `'a` and `T` such that `&'a T` is WF. |
40 | /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. | |
41 | /// | |
42 | /// # Parameters | |
43 | /// | |
44 | /// - `param_env`, the where-clauses in scope | |
45 | /// - `body_id`, the body-id to use when normalizing assoc types. | |
46 | /// Note that this may cause outlives obligations to be injected | |
47 | /// into the inference context with this body-id. | |
48 | /// - `ty`, the type that we are supposed to assume is WF. | |
f2b60f7d | 49 | #[instrument(level = "debug", skip(self, param_env, body_id), ret)] |
ba9703b0 | 50 | fn implied_outlives_bounds( |
8faf50e0 XL |
51 | &self, |
52 | param_env: ty::ParamEnv<'tcx>, | |
9ffffee4 | 53 | body_id: LocalDefId, |
8faf50e0 | 54 | ty: Ty<'tcx>, |
8faf50e0 | 55 | ) -> Vec<OutlivesBound<'tcx>> { |
353b0b11 FG |
56 | let ty = self.resolve_vars_if_possible(ty); |
57 | let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); | |
353b0b11 | 58 | |
49aad941 FG |
59 | // We do not expect existential variables in implied bounds. |
60 | // We may however encounter unconstrained lifetime variables in invalid | |
61 | // code. See #110161 for context. | |
62 | assert!(!ty.has_non_region_infer()); | |
63 | if ty.has_infer() { | |
64 | self.tcx.sess.delay_span_bug( | |
65 | self.tcx.def_span(body_id), | |
66 | "skipped implied_outlives_bounds due to unconstrained lifetimes", | |
67 | ); | |
68 | return vec![]; | |
69 | } | |
70 | ||
71 | let mut canonical_var_values = OriginalQueryValues::default(); | |
72 | let canonical_ty = | |
73 | self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values); | |
74 | let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else { | |
75 | return vec![]; | |
76 | }; | |
77 | ||
78 | let mut constraints = QueryRegionConstraints::default(); | |
79 | let Ok(InferOk { value, obligations }) = self | |
80 | .instantiate_nll_query_response_and_region_obligations( | |
81 | &ObligationCause::dummy(), | |
82 | param_env, | |
83 | &canonical_var_values, | |
84 | canonical_result, | |
85 | &mut constraints, | |
86 | ) else { | |
87 | return vec![]; | |
8faf50e0 | 88 | }; |
49aad941 | 89 | assert_eq!(&obligations, &[]); |
8faf50e0 | 90 | |
49aad941 FG |
91 | if !constraints.is_empty() { |
92 | let span = self.tcx.def_span(body_id); | |
8faf50e0 | 93 | |
f2b60f7d | 94 | debug!(?constraints); |
353b0b11 FG |
95 | if !constraints.member_constraints.is_empty() { |
96 | span_bug!(span, "{:#?}", constraints.member_constraints); | |
97 | } | |
98 | ||
923072b8 FG |
99 | // Instantiation may have produced new inference variables and constraints on those |
100 | // variables. Process these constraints. | |
353b0b11 | 101 | let ocx = ObligationCtxt::new(self); |
923072b8 | 102 | let cause = ObligationCause::misc(span, body_id); |
353b0b11 FG |
103 | for &constraint in &constraints.outlives { |
104 | ocx.register_obligation(self.query_outlives_constraint_to_obligation( | |
105 | constraint, | |
106 | cause.clone(), | |
107 | param_env, | |
108 | )); | |
923072b8 | 109 | } |
353b0b11 FG |
110 | |
111 | let errors = ocx.select_all_or_error(); | |
923072b8 FG |
112 | if !errors.is_empty() { |
113 | self.tcx.sess.delay_span_bug( | |
114 | span, | |
115 | "implied_outlives_bounds failed to solve obligations from instantiation", | |
116 | ); | |
117 | } | |
118 | }; | |
8faf50e0 | 119 | |
49aad941 | 120 | value |
8faf50e0 | 121 | } |
f2b60f7d FG |
122 | |
123 | fn implied_bounds_tys( | |
124 | &'a self, | |
125 | param_env: ParamEnv<'tcx>, | |
9ffffee4 | 126 | body_id: LocalDefId, |
487cf647 | 127 | tys: FxIndexSet<Ty<'tcx>>, |
f2b60f7d | 128 | ) -> Bounds<'a, 'tcx> { |
353b0b11 | 129 | tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty)) |
f2b60f7d | 130 | } |
8faf50e0 | 131 | } |