|  | 
|  | 1 | +// Copyright 2012-2013 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 | +//! Error Reporting for Anonymous Region Lifetime Errors | 
|  | 12 | +//! where both the regions are anonymous. | 
|  | 13 | +use hir; | 
|  | 14 | +use infer::InferCtxt; | 
|  | 15 | +use ty::{self, Region}; | 
|  | 16 | +use infer::region_inference::RegionResolutionError::*; | 
|  | 17 | +use infer::region_inference::RegionResolutionError; | 
|  | 18 | +use hir::map as hir_map; | 
|  | 19 | +use middle::resolve_lifetime as rl; | 
|  | 20 | +use hir::intravisit::{self, Visitor, NestedVisitorMap}; | 
|  | 21 | + | 
|  | 22 | +impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | 
|  | 23 | +    // This method prints the error message for lifetime errors when both the concerned regions | 
|  | 24 | +    // are anonymous. | 
|  | 25 | +    // Consider a case where we have | 
|  | 26 | +    // fn foo(x: &mut Vec<&u8>, y: &u8) | 
|  | 27 | +    //    { x.push(y); }. | 
|  | 28 | +    // The example gives | 
|  | 29 | +    // fn foo(x: &mut Vec<&u8>, y: &u8) { | 
|  | 30 | +    //                    ---      --- these references must have the same lifetime | 
|  | 31 | +    //            x.push(y); | 
|  | 32 | +    //            ^ data from `y` flows into `x` here | 
|  | 33 | +    // It will later be extended to trait objects and structs. | 
|  | 34 | +    pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool { | 
|  | 35 | + | 
|  | 36 | +        let (span, sub, sup) = match *error { | 
|  | 37 | +            ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup), | 
|  | 38 | +            _ => return false, // inapplicable | 
|  | 39 | +        }; | 
|  | 40 | + | 
|  | 41 | +        // Determine whether the sub and sup consist of both anonymous (elided) regions. | 
|  | 42 | +        let (ty1, ty2) = if self.is_suitable_anonymous_region(sup).is_some() && | 
|  | 43 | +                            self.is_suitable_anonymous_region(sub).is_some() { | 
|  | 44 | +            if let (Some(anon_reg1), Some(anon_reg2)) = | 
|  | 45 | +                (self.is_suitable_anonymous_region(sup), self.is_suitable_anonymous_region(sub)) { | 
|  | 46 | +                let ((_, br1), (_, br2)) = (anon_reg1, anon_reg2); | 
|  | 47 | +                if self.find_anon_type(sup, &br1).is_some() && | 
|  | 48 | +                   self.find_anon_type(sub, &br2).is_some() { | 
|  | 49 | +                    (self.find_anon_type(sup, &br1).unwrap(), | 
|  | 50 | +                     self.find_anon_type(sub, &br2).unwrap()) | 
|  | 51 | +                } else { | 
|  | 52 | +                    return false; | 
|  | 53 | +                } | 
|  | 54 | +            } else { | 
|  | 55 | +                return false; | 
|  | 56 | +            } | 
|  | 57 | +        } else { | 
|  | 58 | +            return false; // inapplicable | 
|  | 59 | +        }; | 
|  | 60 | + | 
|  | 61 | +        if let (Some(sup_arg), Some(sub_arg)) = | 
|  | 62 | +            (self.find_arg_with_anonymous_region(sup, sup), | 
|  | 63 | +             self.find_arg_with_anonymous_region(sub, sub)) { | 
|  | 64 | +            let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg); | 
|  | 65 | + | 
|  | 66 | +            let span_label_var1 = if let Some(simple_name) = anon_arg1.pat.simple_name() { | 
|  | 67 | +                format!(" from `{}` ", simple_name) | 
|  | 68 | +            } else { | 
|  | 69 | +                format!(" ") | 
|  | 70 | +            }; | 
|  | 71 | + | 
|  | 72 | +            let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() { | 
|  | 73 | +                format!(" into `{}` ", simple_name) | 
|  | 74 | +            } else { | 
|  | 75 | +                format!(" ") | 
|  | 76 | +            }; | 
|  | 77 | + | 
|  | 78 | +            struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch") | 
|  | 79 | +                .span_label(ty1.span, | 
|  | 80 | +                            format!("these references must have the same lifetime")) | 
|  | 81 | +                .span_label(ty2.span, format!("")) | 
|  | 82 | +                .span_label(span, | 
|  | 83 | +                            format!("data{}flows{}here", span_label_var1, span_label_var2)) | 
|  | 84 | +                .emit(); | 
|  | 85 | +        } else { | 
|  | 86 | +            return false; | 
|  | 87 | +        } | 
|  | 88 | + | 
|  | 89 | +        return true; | 
|  | 90 | +    } | 
|  | 91 | + | 
|  | 92 | +    /// This function calls the `visit_ty` method for the parameters | 
|  | 93 | +    /// corresponding to the anonymous regions. The `nested_visitor.found_type` | 
|  | 94 | +    /// contains the anonymous type. | 
|  | 95 | +    /// | 
|  | 96 | +    /// # Arguments | 
|  | 97 | +    /// | 
|  | 98 | +    /// region - the anonymous region corresponding to the anon_anon conflict | 
|  | 99 | +    /// br - the bound region corresponding to the above region which is of type `BrAnon(_)` | 
|  | 100 | +    /// | 
|  | 101 | +    /// # Example | 
|  | 102 | +    /// ``` | 
|  | 103 | +    /// fn foo(x: &mut Vec<&u8>, y: &u8) | 
|  | 104 | +    ///    { x.push(y); } | 
|  | 105 | +    /// ``` | 
|  | 106 | +    /// The function returns the nested type corresponding to the anonymous region | 
|  | 107 | +    /// for e.g. `&u8` and Vec<`&u8`. | 
|  | 108 | +    fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> { | 
|  | 109 | +        if let Some(anon_reg) = self.is_suitable_anonymous_region(region) { | 
|  | 110 | +            let (def_id, _) = anon_reg; | 
|  | 111 | +            if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { | 
|  | 112 | +                let ret_ty = self.tcx.type_of(def_id); | 
|  | 113 | +                if let ty::TyFnDef(_, _) = ret_ty.sty { | 
|  | 114 | +                    if let hir_map::NodeItem(it) = self.tcx.hir.get(node_id) { | 
|  | 115 | +                        if let hir::ItemFn(ref fndecl, _, _, _, _, _) = it.node { | 
|  | 116 | +                            return fndecl | 
|  | 117 | +                                       .inputs | 
|  | 118 | +                                       .iter() | 
|  | 119 | +                                       .filter_map(|arg| { | 
|  | 120 | +                                let mut nested_visitor = FindNestedTypeVisitor { | 
|  | 121 | +                                    infcx: &self, | 
|  | 122 | +                                    hir_map: &self.tcx.hir, | 
|  | 123 | +                                    bound_region: *br, | 
|  | 124 | +                                    found_type: None, | 
|  | 125 | +                                }; | 
|  | 126 | +                                nested_visitor.visit_ty(&**arg); | 
|  | 127 | +                                if nested_visitor.found_type.is_some() { | 
|  | 128 | +                                    nested_visitor.found_type | 
|  | 129 | +                                } else { | 
|  | 130 | +                                    None | 
|  | 131 | +                                } | 
|  | 132 | +                            }) | 
|  | 133 | +                                       .next(); | 
|  | 134 | +                        } | 
|  | 135 | +                    } | 
|  | 136 | +                } | 
|  | 137 | +            } | 
|  | 138 | +        } | 
|  | 139 | +        None | 
|  | 140 | +    } | 
|  | 141 | +} | 
|  | 142 | + | 
|  | 143 | +// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the | 
|  | 144 | +// anonymous region. The example above would lead to a conflict between | 
|  | 145 | +// the two anonymous lifetimes for &u8 in x and y respectively. This visitor | 
|  | 146 | +// would be invoked twice, once for each lifetime, and would | 
|  | 147 | +// walk the types like &mut Vec<&u8> and &u8 looking for the HIR | 
|  | 148 | +// where that lifetime appears. This allows us to highlight the | 
|  | 149 | +// specific part of the type in the error message. | 
|  | 150 | +struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { | 
|  | 151 | +    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, | 
|  | 152 | +    hir_map: &'a hir::map::Map<'gcx>, | 
|  | 153 | +    // The bound_region corresponding to the Refree(freeregion) | 
|  | 154 | +    // associated with the anonymous region we are looking for. | 
|  | 155 | +    bound_region: ty::BoundRegion, | 
|  | 156 | +    // The type where the anonymous lifetime appears | 
|  | 157 | +    // for e.g. Vec<`&u8`> and <`&u8`> | 
|  | 158 | +    found_type: Option<&'gcx hir::Ty>, | 
|  | 159 | +} | 
|  | 160 | + | 
|  | 161 | +impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { | 
|  | 162 | +    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { | 
|  | 163 | +        NestedVisitorMap::OnlyBodies(&self.hir_map) | 
|  | 164 | +    } | 
|  | 165 | + | 
|  | 166 | +    fn visit_ty(&mut self, arg: &'gcx hir::Ty) { | 
|  | 167 | +        // Find the index of the anonymous region that was part of the | 
|  | 168 | +        // error. We will then search the function parameters for a bound | 
|  | 169 | +        // region at the right depth with the same index. | 
|  | 170 | +        let br_index = match self.bound_region { | 
|  | 171 | +            ty::BrAnon(index) => index, | 
|  | 172 | +            _ => return, | 
|  | 173 | +        }; | 
|  | 174 | + | 
|  | 175 | +        match arg.node { | 
|  | 176 | +            hir::TyRptr(ref lifetime, _) => { | 
|  | 177 | +                match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) { | 
|  | 178 | +                    // the lifetime of the TyRptr | 
|  | 179 | +                    Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => { | 
|  | 180 | +                        if debuijn_index.depth == 1 && anon_index == br_index { | 
|  | 181 | +                            self.found_type = Some(arg); | 
|  | 182 | +                            return; // we can stop visiting now | 
|  | 183 | +                        } | 
|  | 184 | +                    } | 
|  | 185 | +                    Some(&rl::Region::Static) | | 
|  | 186 | +                    Some(&rl::Region::EarlyBound(_, _)) | | 
|  | 187 | +                    Some(&rl::Region::LateBound(_, _)) | | 
|  | 188 | +                    Some(&rl::Region::Free(_, _)) | | 
|  | 189 | +                    None => { | 
|  | 190 | +                        debug!("no arg found"); | 
|  | 191 | +                    } | 
|  | 192 | +                } | 
|  | 193 | +            } | 
|  | 194 | +            _ => {} | 
|  | 195 | +        } | 
|  | 196 | +        // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`, | 
|  | 197 | +        // go on to visit `&Foo` | 
|  | 198 | +        intravisit::walk_ty(self, arg); | 
|  | 199 | +    } | 
|  | 200 | +} | 
0 commit comments