|
| 1 | +// Copyright 2017 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 | +use borrow_check::nll::region_infer::RegionInferenceContext; |
| 12 | +use borrow_check::nll::ToRegionVid; |
| 13 | +use rustc::hir::def_id::DefId; |
| 14 | +use rustc::mir::{Local, Mir}; |
| 15 | +use rustc::ty::{self, RegionVid, TyCtxt}; |
| 16 | +use rustc_data_structures::indexed_vec::Idx; |
| 17 | +use rustc_errors::DiagnosticBuilder; |
| 18 | +use syntax::ast::Name; |
| 19 | +use syntax::symbol::keywords; |
| 20 | +use syntax_pos::symbol::InternedString; |
| 21 | + |
| 22 | +impl<'tcx> RegionInferenceContext<'tcx> { |
| 23 | + /// Maps from an internal MIR region vid to something that we can |
| 24 | + /// report to the user. In some cases, the region vids will map |
| 25 | + /// directly to lifetimes that the user has a name for (e.g., |
| 26 | + /// `'static`). But frequently they will not, in which case we |
| 27 | + /// have to find some way to identify the lifetime to the user. To |
| 28 | + /// that end, this function takes a "diagnostic" so that it can |
| 29 | + /// create auxiliary notes as needed. |
| 30 | + /// |
| 31 | + /// Example (function arguments): |
| 32 | + /// |
| 33 | + /// Suppose we are trying to give a name to the lifetime of the |
| 34 | + /// reference `x`: |
| 35 | + /// |
| 36 | + /// ``` |
| 37 | + /// fn foo(x: &u32) { .. } |
| 38 | + /// ``` |
| 39 | + /// |
| 40 | + /// This function would create a label like this: |
| 41 | + /// |
| 42 | + /// ``` |
| 43 | + /// | fn foo(x: &u32) { .. } |
| 44 | + /// ------- fully elaborated type of `x` is `&'1 u32` |
| 45 | + /// ``` |
| 46 | + /// |
| 47 | + /// and then return the name `'1` for us to use. |
| 48 | + crate fn give_region_a_name( |
| 49 | + &self, |
| 50 | + tcx: TyCtxt<'_, '_, 'tcx>, |
| 51 | + mir: &Mir<'tcx>, |
| 52 | + mir_def_id: DefId, |
| 53 | + fr: RegionVid, |
| 54 | + counter: &mut usize, |
| 55 | + diag: &mut DiagnosticBuilder, |
| 56 | + ) -> InternedString { |
| 57 | + debug!("give_region_a_name(fr={:?}, counter={})", fr, counter); |
| 58 | + |
| 59 | + assert!(self.universal_regions.is_universal_region(fr)); |
| 60 | + |
| 61 | + self.give_name_from_error_region(tcx, mir_def_id, fr, counter, diag) |
| 62 | + .or_else(|| { |
| 63 | + self.give_name_if_anonymous_region_appears_in_arguments(tcx, mir, fr, counter, diag) |
| 64 | + }) |
| 65 | + .or_else(|| { |
| 66 | + self.give_name_if_anonymous_region_appears_in_upvars(tcx, mir, fr, counter, diag) |
| 67 | + }) |
| 68 | + .or_else(|| { |
| 69 | + self.give_name_if_anonymous_region_appears_in_output(tcx, mir, fr, counter, diag) |
| 70 | + }) |
| 71 | + .unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr)) |
| 72 | + } |
| 73 | + |
| 74 | + /// Check for the case where `fr` maps to something that the |
| 75 | + /// *user* has a name for. In that case, we'll be able to map |
| 76 | + /// `fr` to a `Region<'tcx>`, and that region will be one of |
| 77 | + /// named variants. |
| 78 | + fn give_name_from_error_region( |
| 79 | + &self, |
| 80 | + tcx: TyCtxt<'_, '_, 'tcx>, |
| 81 | + mir_def_id: DefId, |
| 82 | + fr: RegionVid, |
| 83 | + counter: &mut usize, |
| 84 | + diag: &mut DiagnosticBuilder<'_>, |
| 85 | + ) -> Option<InternedString> { |
| 86 | + let error_region = self.to_error_region(fr)?; |
| 87 | + debug!("give_region_a_name: error_region = {:?}", error_region); |
| 88 | + match error_region { |
| 89 | + ty::ReEarlyBound(ebr) => Some(ebr.name), |
| 90 | + |
| 91 | + ty::ReStatic => Some(keywords::StaticLifetime.name().as_interned_str()), |
| 92 | + |
| 93 | + ty::ReFree(free_region) => match free_region.bound_region { |
| 94 | + ty::BoundRegion::BrNamed(_, name) => Some(name), |
| 95 | + |
| 96 | + ty::BoundRegion::BrEnv => { |
| 97 | + let closure_span = tcx.hir.span_if_local(mir_def_id).unwrap(); |
| 98 | + let region_name = self.synthesize_region_name(counter); |
| 99 | + diag.span_label( |
| 100 | + closure_span, |
| 101 | + format!("lifetime `{}` represents the closure body", region_name), |
| 102 | + ); |
| 103 | + Some(region_name) |
| 104 | + } |
| 105 | + |
| 106 | + ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None, |
| 107 | + }, |
| 108 | + |
| 109 | + ty::ReLateBound(..) |
| 110 | + | ty::ReScope(..) |
| 111 | + | ty::ReVar(..) |
| 112 | + | ty::ReSkolemized(..) |
| 113 | + | ty::ReEmpty |
| 114 | + | ty::ReErased |
| 115 | + | ty::ReClosureBound(..) |
| 116 | + | ty::ReCanonical(..) => None, |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + /// Find an argument that contains `fr` and label it with a fully |
| 121 | + /// elaborated type, returning something like `'1`. Result looks |
| 122 | + /// like: |
| 123 | + /// |
| 124 | + /// ``` |
| 125 | + /// | fn foo(x: &u32) { .. } |
| 126 | + /// ------- fully elaborated type of `x` is `&'1 u32` |
| 127 | + /// ``` |
| 128 | + fn give_name_if_anonymous_region_appears_in_arguments( |
| 129 | + &self, |
| 130 | + tcx: TyCtxt<'_, '_, 'tcx>, |
| 131 | + mir: &Mir<'tcx>, |
| 132 | + fr: RegionVid, |
| 133 | + counter: &mut usize, |
| 134 | + diag: &mut DiagnosticBuilder<'_>, |
| 135 | + ) -> Option<InternedString> { |
| 136 | + let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs(); |
| 137 | + let argument_index = self.universal_regions |
| 138 | + .unnormalized_input_tys |
| 139 | + .iter() |
| 140 | + .skip(implicit_inputs) |
| 141 | + .position(|arg_ty| { |
| 142 | + debug!("give_name_if_anonymous_region_appears_in_arguments: arg_ty = {:?}", arg_ty); |
| 143 | + tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr) |
| 144 | + })? |
| 145 | + + implicit_inputs; |
| 146 | + |
| 147 | + debug!( |
| 148 | + "give_name_if_anonymous_region_appears_in_arguments: \ |
| 149 | + found {:?} in argument {} which has type {:?}", |
| 150 | + fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index], |
| 151 | + ); |
| 152 | + |
| 153 | + let region_name = self.synthesize_region_name(counter); |
| 154 | + |
| 155 | + let argument_local = Local::new(argument_index + 1); |
| 156 | + let argument_span = mir.local_decls[argument_local].source_info.span; |
| 157 | + diag.span_label( |
| 158 | + argument_span, |
| 159 | + format!("lifetime `{}` appears in this argument", region_name,), |
| 160 | + ); |
| 161 | + |
| 162 | + Some(region_name) |
| 163 | + } |
| 164 | + |
| 165 | + /// Find a closure upvar that contains `fr` and label it with a |
| 166 | + /// fully elaborated type, returning something like `'1`. Result |
| 167 | + /// looks like: |
| 168 | + /// |
| 169 | + /// ``` |
| 170 | + /// | let x = Some(&22); |
| 171 | + /// - fully elaborated type of `x` is `Option<&'1 u32>` |
| 172 | + /// ``` |
| 173 | + fn give_name_if_anonymous_region_appears_in_upvars( |
| 174 | + &self, |
| 175 | + tcx: TyCtxt<'_, '_, 'tcx>, |
| 176 | + mir: &Mir<'tcx>, |
| 177 | + fr: RegionVid, |
| 178 | + counter: &mut usize, |
| 179 | + diag: &mut DiagnosticBuilder<'_>, |
| 180 | + ) -> Option<InternedString> { |
| 181 | + let upvar_index = self.universal_regions |
| 182 | + .defining_ty |
| 183 | + .upvar_tys(tcx) |
| 184 | + .position(|upvar_ty| { |
| 185 | + debug!( |
| 186 | + "give_name_if_anonymous_region_appears_in_upvars: upvar_ty = {:?}", |
| 187 | + upvar_ty, |
| 188 | + ); |
| 189 | + tcx.any_free_region_meets(&upvar_ty, |r| r.to_region_vid() == fr) |
| 190 | + })?; |
| 191 | + |
| 192 | + debug!( |
| 193 | + "give_name_if_anonymous_region_appears_in_upvars: \ |
| 194 | + found {:?} in upvar {} which has type {:?}", |
| 195 | + fr, |
| 196 | + upvar_index, |
| 197 | + self.universal_regions |
| 198 | + .defining_ty |
| 199 | + .upvar_tys(tcx) |
| 200 | + .nth(upvar_index), |
| 201 | + ); |
| 202 | + |
| 203 | + let region_name = self.synthesize_region_name(counter); |
| 204 | + |
| 205 | + let upvar_hir_id = mir.upvar_decls[upvar_index].var_hir_id.assert_crate_local(); |
| 206 | + let upvar_node_id = tcx.hir.hir_to_node_id(upvar_hir_id); |
| 207 | + let upvar_span = tcx.hir.span(upvar_node_id); |
| 208 | + let upvar_name = tcx.hir.name(upvar_node_id); |
| 209 | + diag.span_label( |
| 210 | + upvar_span, |
| 211 | + format!( |
| 212 | + "lifetime `{}` appears in the type of `{}`", |
| 213 | + region_name, upvar_name, |
| 214 | + ), |
| 215 | + ); |
| 216 | + |
| 217 | + Some(region_name) |
| 218 | + } |
| 219 | + |
| 220 | + /// Check for arguments appearing in the (closure) return type. It |
| 221 | + /// must be a closure since, in a free fn, such an argument would |
| 222 | + /// have to either also appear in an argument (if using elision) |
| 223 | + /// or be early bound (named, not in argument). |
| 224 | + fn give_name_if_anonymous_region_appears_in_output( |
| 225 | + &self, |
| 226 | + tcx: TyCtxt<'_, '_, 'tcx>, |
| 227 | + mir: &Mir<'tcx>, |
| 228 | + fr: RegionVid, |
| 229 | + counter: &mut usize, |
| 230 | + diag: &mut DiagnosticBuilder<'_>, |
| 231 | + ) -> Option<InternedString> { |
| 232 | + let return_ty = self.universal_regions |
| 233 | + .unnormalized_output_ty; |
| 234 | + debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty); |
| 235 | + if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) { |
| 236 | + return None; |
| 237 | + } |
| 238 | + |
| 239 | + let region_name = self.synthesize_region_name(counter); |
| 240 | + diag.span_label( |
| 241 | + mir.span, |
| 242 | + format!("lifetime `{}` appears in return type", region_name), |
| 243 | + ); |
| 244 | + |
| 245 | + Some(region_name) |
| 246 | + } |
| 247 | + |
| 248 | + /// Create a synthetic region named `'1`, incrementing the |
| 249 | + /// counter. |
| 250 | + fn synthesize_region_name(&self, counter: &mut usize) -> InternedString { |
| 251 | + let c = *counter; |
| 252 | + *counter += 1; |
| 253 | + |
| 254 | + Name::intern(&format!("'{:?}", c)).as_interned_str() |
| 255 | + } |
| 256 | +} |
0 commit comments