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.
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.
11 // #![warn(deprecated_mode)]
13 pub use self::WfConstraint::*;
15 use middle::subst::{ParamSpace, Subst, Substs};
16 use middle::ty::{self, Ty};
17 use middle::ty_fold::{TypeFolder};
21 use util::ppaux::Repr;
23 // Helper functions related to manipulating region types.
25 pub enum WfConstraint<'tcx> {
26 RegionSubRegionConstraint(Option<Ty<'tcx>>, ty::Region, ty::Region),
27 RegionSubParamConstraint(Option<Ty<'tcx>>, ty::Region, ty::ParamTy),
30 struct Wf<'a, 'tcx: 'a> {
31 tcx: &'a ty::ctxt<'tcx>,
32 stack: Vec<(ty::Region, Option<Ty<'tcx>>)>,
33 out: Vec<WfConstraint<'tcx>>,
36 /// This routine computes the well-formedness constraints that must hold for the type `ty` to
37 /// appear in a context with lifetime `outer_region`
38 pub fn region_wf_constraints<'tcx>(
41 outer_region: ty::Region)
42 -> Vec<WfConstraint<'tcx>>
44 let mut stack = Vec::new();
45 stack.push((outer_region, None));
46 let mut wf = Wf { tcx: tcx,
49 wf.accumulate_from_ty(ty);
53 impl<'a, 'tcx> Wf<'a, 'tcx> {
54 fn accumulate_from_ty(&mut self, ty: Ty<'tcx>) {
55 debug!("Wf::accumulate_from_ty(ty={})",
67 // No borrowed content reachable here.
70 ty::ty_closure(box ref c) => {
71 self.accumulate_from_closure_ty(ty, c);
74 ty::ty_unboxed_closure(_, region, _) => {
75 // An "unboxed closure type" is basically
76 // modeled here as equivalent to a struct like
78 // struct TheClosure<'b> {
82 // where the `'b` is the lifetime bound of the
83 // contents (i.e., all contents must outlive 'b).
85 // Even though unboxed closures are glorified structs
86 // of upvars, we do not need to consider them as they
87 // can't generate any new constraints. The
88 // substitutions on the closure are equal to the free
89 // substitutions of the enclosing parameter
90 // environment. An upvar captured by value has the
91 // same type as the original local variable which is
92 // already checked for consistency. If the upvar is
93 // captured by reference it must also outlive the
94 // region bound on the closure, but this is explicitly
95 // handled by logic in regionck.
96 self.push_region_constraint_from_top(*region);
99 ty::ty_trait(ref t) => {
100 let required_region_bounds =
101 ty::object_region_bounds(self.tcx, Some(&t.principal), t.bounds.builtin_bounds);
102 self.accumulate_from_object_ty(ty, t.bounds.region_bound, required_region_bounds)
105 ty::ty_enum(def_id, substs) |
106 ty::ty_struct(def_id, substs) => {
107 let item_scheme = ty::lookup_item_type(self.tcx, def_id);
108 self.accumulate_from_adt(ty, def_id, &item_scheme.generics, substs)
112 ty::ty_ptr(ty::mt { ty: t, .. }) |
114 self.accumulate_from_ty(t)
117 ty::ty_rptr(r_b, mt) => {
118 self.accumulate_from_rptr(ty, *r_b, mt.ty);
122 self.push_param_constraint_from_top(p);
125 ty::ty_projection(ref data) => {
126 // `<T as TraitRef<..>>::Name`
128 // FIXME(#20303) -- gain ability to require that ty_projection : in-scope region,
129 // like a type parameter
131 // this seems like a minimal requirement:
132 let trait_def = ty::lookup_trait_def(self.tcx, data.trait_ref.def_id);
133 self.accumulate_from_adt(ty, data.trait_ref.def_id,
134 &trait_def.generics, data.trait_ref.substs)
137 ty::ty_tup(ref tuptys) => {
138 for &tupty in tuptys.iter() {
139 self.accumulate_from_ty(tupty);
144 // This should not happen, BUT:
146 // Currently we uncover region relationships on
147 // entering the fn check. We should do this after
148 // the fn check, then we can call this case a bug().
153 format!("Unexpected type encountered while doing wf check: {}",
154 ty.repr(self.tcx))[]);
159 fn accumulate_from_rptr(&mut self,
163 // We are walking down a type like this, and current
164 // position is indicated by caret:
169 // At this point, top of stack will be `'a`. We must
170 // require that `'a <= 'b`.
172 self.push_region_constraint_from_top(r_b);
174 // Now we push `'b` onto the stack, because it must
175 // constrain any borrowed content we find within `T`.
177 self.stack.push((r_b, Some(ty)));
178 self.accumulate_from_ty(ty_b);
179 self.stack.pop().unwrap();
182 /// Pushes a constraint that `r_b` must outlive the top region on the stack.
183 fn push_region_constraint_from_top(&mut self,
186 // Indicates that we have found borrowed content with a lifetime
187 // of at least `r_b`. This adds a constraint that `r_b` must
188 // outlive the region `r_a` on top of the stack.
190 // As an example, imagine walking a type like:
195 // when we hit the inner pointer (indicated by caret), `'a` will
196 // be on top of stack and `'b` will be the lifetime of the content
197 // we just found. So we add constraint that `'a <= 'b`.
199 let &(r_a, opt_ty) = self.stack.last().unwrap();
200 self.push_sub_region_constraint(opt_ty, r_a, r_b);
203 /// Pushes a constraint that `r_a <= r_b`, due to `opt_ty`
204 fn push_sub_region_constraint(&mut self,
205 opt_ty: Option<Ty<'tcx>>,
208 self.out.push(RegionSubRegionConstraint(opt_ty, r_a, r_b));
211 /// Pushes a constraint that `param_ty` must outlive the top region on the stack.
212 fn push_param_constraint_from_top(&mut self,
213 param_ty: ty::ParamTy) {
214 let &(region, opt_ty) = self.stack.last().unwrap();
215 self.push_param_constraint(region, opt_ty, param_ty);
218 /// Pushes a constraint that `region <= param_ty`, due to `opt_ty`
219 fn push_param_constraint(&mut self,
221 opt_ty: Option<Ty<'tcx>>,
222 param_ty: ty::ParamTy) {
223 self.out.push(RegionSubParamConstraint(opt_ty, region, param_ty));
226 fn accumulate_from_adt(&mut self,
229 generics: &ty::Generics<'tcx>,
230 substs: &Substs<'tcx>)
232 // The generic declarations from the type, appropriately
233 // substituted for the actual substitutions.
234 let generics = generics.subst(self.tcx, substs);
236 // Variance of each type/region parameter.
237 let variances = ty::item_variances(self.tcx, def_id);
239 for &space in ParamSpace::all().iter() {
240 let region_params = substs.regions().get_slice(space);
241 let region_variances = variances.regions.get_slice(space);
242 let region_param_defs = generics.regions.get_slice(space);
243 assert_eq!(region_params.len(), region_variances.len());
244 for (®ion_param, (®ion_variance, region_param_def)) in
245 region_params.iter().zip(
246 region_variances.iter().zip(
247 region_param_defs.iter()))
249 match region_variance {
250 ty::Covariant | ty::Bivariant => {
251 // Ignore covariant or bivariant region
252 // parameters. To understand why, consider a
253 // struct `Foo<'a>`. If `Foo` contains any
254 // references with lifetime `'a`, then `'a` must
255 // be at least contravariant (and possibly
256 // invariant). The only way to have a covariant
257 // result is if `Foo` contains only a field with a
258 // type like `fn() -> &'a T`; i.e., a bare
259 // function that can produce a reference of
260 // lifetime `'a`. In this case, there is no
261 // *actual data* with lifetime `'a` that is
262 // reachable. (Presumably this bare function is
263 // really returning static data.)
266 ty::Contravariant | ty::Invariant => {
267 // If the parameter is contravariant or
268 // invariant, there may indeed be reachable
269 // data with this lifetime. See other case for
271 self.push_region_constraint_from_top(region_param);
275 for ®ion_bound in region_param_def.bounds.iter() {
276 // The type declared a constraint like
280 // which means that `'a <= 'b` (after
281 // substitution). So take the region we
282 // substituted for `'a` (`region_bound`) and make
283 // it a subregion of the region we substituted
284 // `'b` (`region_param`).
285 self.push_sub_region_constraint(
286 Some(ty), region_bound, region_param);
290 let types = substs.types.get_slice(space);
291 let type_variances = variances.types.get_slice(space);
292 let type_param_defs = generics.types.get_slice(space);
293 assert_eq!(types.len(), type_variances.len());
294 for (&type_param_ty, (&variance, type_param_def)) in
296 type_variances.iter().zip(
297 type_param_defs.iter()))
299 debug!("type_param_ty={} variance={}",
300 type_param_ty.repr(self.tcx),
301 variance.repr(self.tcx));
304 ty::Contravariant | ty::Bivariant => {
305 // As above, except that in this it is a
306 // *contravariant* reference that indices that no
307 // actual data of type T is reachable.
310 ty::Covariant | ty::Invariant => {
311 self.accumulate_from_ty(type_param_ty);
315 // Inspect bounds on this type parameter for any
317 for &r in type_param_def.bounds.region_bounds.iter() {
318 self.stack.push((r, Some(ty)));
319 self.accumulate_from_ty(type_param_ty);
320 self.stack.pop().unwrap();
326 fn accumulate_from_closure_ty(&mut self,
328 c: &ty::ClosureTy<'tcx>)
331 ty::RegionTraitStore(r_b, _) => {
332 self.push_region_constraint_from_top(r_b);
334 ty::UniqTraitStore => { }
337 let required_region_bounds =
338 ty::object_region_bounds(self.tcx, None, c.bounds.builtin_bounds);
339 self.accumulate_from_object_ty(ty, c.bounds.region_bound, required_region_bounds);
342 fn accumulate_from_object_ty(&mut self,
344 region_bound: ty::Region,
345 required_region_bounds: Vec<ty::Region>)
347 // Imagine a type like this:
350 // trait Bar<'c> : 'c { }
352 // &'b (Foo+'c+Bar<'d>)
355 // In this case, the following relationships must hold:
360 // The first conditions is due to the normal region pointer
361 // rules, which say that a reference cannot outlive its
364 // The final condition may be a bit surprising. In particular,
365 // you may expect that it would have been `'c <= 'd`, since
366 // usually lifetimes of outer things are conservative
367 // approximations for inner things. However, it works somewhat
368 // differently with trait objects: here the idea is that if the
369 // user specifies a region bound (`'c`, in this case) it is the
370 // "master bound" that *implies* that bounds from other traits are
371 // all met. (Remember that *all bounds* in a type like
372 // `Foo+Bar+Zed` must be met, not just one, hence if we write
373 // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
376 // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
377 // am looking forward to the future here.
379 // The content of this object type must outlive
380 // `bounds.region_bound`:
381 let r_c = region_bound;
382 self.push_region_constraint_from_top(r_c);
384 // And then, in turn, to be well-formed, the
385 // `region_bound` that user specified must imply the
386 // region bounds required from all of the trait types:
387 for &r_d in required_region_bounds.iter() {
388 // Each of these is an instance of the `'c <= 'b`
390 self.out.push(RegionSubRegionConstraint(Some(ty), r_d, r_c));
395 impl<'tcx> Repr<'tcx> for WfConstraint<'tcx> {
396 fn repr(&self, tcx: &ty::ctxt) -> String {
398 RegionSubRegionConstraint(_, r_a, r_b) => {
399 format!("RegionSubRegionConstraint({}, {})",
404 RegionSubParamConstraint(_, r, p) => {
405 format!("RegionSubParamConstraint({}, {})",