|
| 1 | +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +// The outlines relation `T: 'a` or `'a: 'b`. |
| 12 | + |
| 13 | +use middle::infer::InferCtxt; |
| 14 | +use middle::ty::{self, RegionEscape, Ty}; |
| 15 | + |
| 16 | +#[derive(Debug)] |
| 17 | +pub enum Component<'tcx> { |
| 18 | + Region(ty::Region), |
| 19 | + Param(ty::ParamTy), |
| 20 | + UnresolvedInferenceVariable(ty::InferTy), |
| 21 | + |
| 22 | + // Projections like `T::Foo` are tricky because a constraint like |
| 23 | + // `T::Foo: 'a` can be satisfied in so many ways. There may be a |
| 24 | + // where-clause that says `T::Foo: 'a`, or the defining trait may |
| 25 | + // include a bound like `type Foo: 'static`, or -- in the most |
| 26 | + // conservative way -- we can prove that `T: 'a` (more generally, |
| 27 | + // that all components in the projection outlive `'a`). This code |
| 28 | + // is not in a position to judge which is the best technique, so |
| 29 | + // we just product the projection as a component and leave it to |
| 30 | + // the consumer to decide (but see `EscapingProjection` below). |
| 31 | + Projection(ty::ProjectionTy<'tcx>), |
| 32 | + |
| 33 | + // In the case where a projection has escaping regions -- meaning |
| 34 | + // regions bound within the type itself -- we always use |
| 35 | + // the most conservative rule, which requires that all components |
| 36 | + // outlive the bound. So for example if we had a type like this: |
| 37 | + // |
| 38 | + // for<'a> Trait1< <T as Trait2<'a,'b>>::Foo > |
| 39 | + // ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 40 | + // |
| 41 | + // then the inner projection (underlined) has an escaping region |
| 42 | + // `'a`. We consider that outer trait `'c` to meet a bound if `'b` |
| 43 | + // outlives `'b: 'c`, and we don't consider whether the trait |
| 44 | + // declares that `Foo: 'static` etc. Therefore, we just return the |
| 45 | + // free components of such a projection (in this case, `'b`). |
| 46 | + // |
| 47 | + // However, in the future, we may want to get smarter, and |
| 48 | + // actually return a "higher-ranked projection" here. Therefore, |
| 49 | + // we mark that these components are part of an escaping |
| 50 | + // projection, so that implied bounds code can avoid relying on |
| 51 | + // them. This gives us room to improve the regionck reasoning in |
| 52 | + // the future without breaking backwards compat. |
| 53 | + EscapingProjection(Vec<Component<'tcx>>), |
| 54 | + |
| 55 | + RFC1214(Vec<Component<'tcx>>), |
| 56 | +} |
| 57 | + |
| 58 | +/// Returns all the things that must outlive `'a` for the condition |
| 59 | +/// `ty0: 'a` to hold. |
| 60 | +pub fn components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, |
| 61 | + ty0: Ty<'tcx>) |
| 62 | + -> Vec<Component<'tcx>> { |
| 63 | + let mut components = vec![]; |
| 64 | + compute_components(infcx, ty0, &mut components); |
| 65 | + debug!("outlives({:?}) = {:?}", ty0, components); |
| 66 | + components |
| 67 | +} |
| 68 | + |
| 69 | +fn compute_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, |
| 70 | + ty0: Ty<'tcx>, |
| 71 | + out: &mut Vec<Component<'tcx>>) { |
| 72 | + // Descend through the types, looking for the various "base" |
| 73 | + // components and collecting them into `out`. This is not written |
| 74 | + // with `collect()` because of the need to sometimes skip subtrees |
| 75 | + // in the `subtys` iterator (e.g., when encountering a |
| 76 | + // projection). |
| 77 | + let mut subtys = ty0.walk(); |
| 78 | + while let Some(ty) = subtys.next() { |
| 79 | + match ty.sty { |
| 80 | + ty::TyClosure(_, ref substs) => { |
| 81 | + // FIXME(#27086). We do not accumulate from substs, since they |
| 82 | + // don't represent reachable data. This means that, in |
| 83 | + // practice, some of the lifetime parameters might not |
| 84 | + // be in scope when the body runs, so long as there is |
| 85 | + // no reachable data with that lifetime. For better or |
| 86 | + // worse, this is consistent with fn types, however, |
| 87 | + // which can also encapsulate data in this fashion |
| 88 | + // (though it's somewhat harder, and typically |
| 89 | + // requires virtual dispatch). |
| 90 | + // |
| 91 | + // Note that changing this (in a naive way, at least) |
| 92 | + // causes regressions for what appears to be perfectly |
| 93 | + // reasonable code like this: |
| 94 | + // |
| 95 | + // ``` |
| 96 | + // fn foo<'a>(p: &Data<'a>) { |
| 97 | + // bar(|q: &mut Parser| q.read_addr()) |
| 98 | + // } |
| 99 | + // fn bar(p: Box<FnMut(&mut Parser)+'static>) { |
| 100 | + // } |
| 101 | + // ``` |
| 102 | + // |
| 103 | + // Note that `p` (and `'a`) are not used in the |
| 104 | + // closure at all, but to meet the requirement that |
| 105 | + // the closure type `C: 'static` (so it can be coerced |
| 106 | + // to the object type), we get the requirement that |
| 107 | + // `'a: 'static` since `'a` appears in the closure |
| 108 | + // type `C`. |
| 109 | + // |
| 110 | + // A smarter fix might "prune" unused `func_substs` -- |
| 111 | + // this would avoid breaking simple examples like |
| 112 | + // this, but would still break others (which might |
| 113 | + // indeed be invalid, depending on your POV). Pruning |
| 114 | + // would be a subtle process, since we have to see |
| 115 | + // what func/type parameters are used and unused, |
| 116 | + // taking into consideration UFCS and so forth. |
| 117 | + |
| 118 | + for &upvar_ty in &substs.upvar_tys { |
| 119 | + compute_components(infcx, upvar_ty, out); |
| 120 | + } |
| 121 | + subtys.skip_current_subtree(); |
| 122 | + } |
| 123 | + ty::TyBareFn(..) | ty::TyTrait(..) => { |
| 124 | + subtys.skip_current_subtree(); |
| 125 | + let temp = capture_components(infcx, ty); |
| 126 | + out.push(Component::RFC1214(temp)); |
| 127 | + } |
| 128 | + ty::TyParam(p) => { |
| 129 | + out.push(Component::Param(p)); |
| 130 | + subtys.skip_current_subtree(); |
| 131 | + } |
| 132 | + ty::TyProjection(ref data) => { |
| 133 | + // For projections, we prefer to generate an |
| 134 | + // obligation like `<P0 as Trait<P1...Pn>>::Foo: 'a`, |
| 135 | + // because this gives the regionck more ways to prove |
| 136 | + // that it holds. However, regionck is not (at least |
| 137 | + // currently) prepared to deal with higher-ranked |
| 138 | + // regions that may appear in the |
| 139 | + // trait-ref. Therefore, if we see any higher-ranke |
| 140 | + // regions, we simply fallback to the most restrictive |
| 141 | + // rule, which requires that `Pi: 'a` for all `i`. |
| 142 | + |
| 143 | + if !data.has_escaping_regions() { |
| 144 | + // best case: no escaping reions, so push the |
| 145 | + // projection and skip the subtree (thus |
| 146 | + // generating no constraints for Pi). |
| 147 | + out.push(Component::Projection(*data)); |
| 148 | + } else { |
| 149 | + // fallback case: continue walking through and |
| 150 | + // constrain Pi. |
| 151 | + let temp = capture_components(infcx, ty); |
| 152 | + out.push(Component::EscapingProjection(temp)); |
| 153 | + } |
| 154 | + subtys.skip_current_subtree(); |
| 155 | + } |
| 156 | + ty::TyInfer(_) => { |
| 157 | + let ty = infcx.resolve_type_vars_if_possible(&ty); |
| 158 | + if let ty::TyInfer(infer_ty) = ty.sty { |
| 159 | + out.push(Component::UnresolvedInferenceVariable(infer_ty)); |
| 160 | + } else { |
| 161 | + compute_components(infcx, ty, out); |
| 162 | + } |
| 163 | + } |
| 164 | + _ => { |
| 165 | + // for all other types, just constrain the regions and |
| 166 | + // keep walking to find any other types. |
| 167 | + push_region_constraints(out, ty.regions()); |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +fn capture_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, |
| 174 | + ty: Ty<'tcx>) |
| 175 | + -> Vec<Component<'tcx>> { |
| 176 | + let mut temp = vec![]; |
| 177 | + push_region_constraints(&mut temp, ty.regions()); |
| 178 | + for subty in ty.walk_shallow() { |
| 179 | + compute_components(infcx, subty, &mut temp); |
| 180 | + } |
| 181 | + temp |
| 182 | +} |
| 183 | + |
| 184 | +fn push_region_constraints<'tcx>(out: &mut Vec<Component<'tcx>>, regions: Vec<ty::Region>) { |
| 185 | + for r in regions { |
| 186 | + if !r.is_bound() { |
| 187 | + out.push(Component::Region(r)); |
| 188 | + } |
| 189 | + } |
| 190 | +} |
| 191 | + |
0 commit comments