]>
Commit | Line | Data |
---|---|---|
60c5eb7d XL |
1 | //! The entry point of the NLL borrow checker. |
2 | ||
74b04a01 | 3 | use rustc_data_structures::fx::FxHashMap; |
8faf50e0 | 4 | use rustc_errors::Diagnostic; |
dfeec247 XL |
5 | use rustc_hir::def_id::DefId; |
6 | use rustc_index::vec::IndexVec; | |
74b04a01 | 7 | use rustc_infer::infer::InferCtxt; |
ba9703b0 XL |
8 | use rustc_middle::mir::{ |
9 | BasicBlock, Body, BodyAndCache, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, | |
10 | Location, Promoted, ReadOnlyBodyAndCache, | |
11 | }; | |
12 | use rustc_middle::ty::{self, RegionKind, RegionVid}; | |
dfeec247 | 13 | use rustc_span::symbol::sym; |
94b46f34 | 14 | use std::env; |
dfeec247 | 15 | use std::fmt::Debug; |
ff7c6d11 | 16 | use std::io; |
83c7162d | 17 | use std::path::PathBuf; |
94b46f34 XL |
18 | use std::rc::Rc; |
19 | use std::str::FromStr; | |
ff7c6d11 | 20 | |
83c7162d | 21 | use self::mir_util::PassWhere; |
94b46f34 | 22 | use polonius_engine::{Algorithm, Output}; |
60c5eb7d | 23 | |
dfeec247 | 24 | use crate::dataflow::move_paths::{InitKind, InitLocation, MoveData}; |
60c5eb7d | 25 | use crate::dataflow::MaybeInitializedPlaces; |
ba9703b0 | 26 | use crate::dataflow::ResultsCursor; |
60c5eb7d | 27 | use crate::transform::MirSource; |
dfeec247 XL |
28 | use crate::util as mir_util; |
29 | use crate::util::pretty; | |
ff7c6d11 | 30 | |
60c5eb7d XL |
31 | use crate::borrow_check::{ |
32 | borrow_set::BorrowSet, | |
dfeec247 XL |
33 | constraint_generation, |
34 | diagnostics::RegionErrors, | |
60c5eb7d | 35 | facts::{AllFacts, AllFactsExt, RustcFacts}, |
dfeec247 XL |
36 | invalidation, |
37 | location::LocationTable, | |
38 | region_infer::{values::RegionValueElements, RegionInferenceContext}, | |
39 | renumber, | |
40 | type_check::{self, MirTypeckRegionConstraints, MirTypeckResults}, | |
60c5eb7d | 41 | universal_regions::UniversalRegions, |
60c5eb7d | 42 | }; |
ff7c6d11 | 43 | |
60c5eb7d | 44 | crate type PoloniusOutput = Output<RustcFacts>; |
ff7c6d11 | 45 | |
dfeec247 XL |
46 | /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any |
47 | /// closure requirements to propagate, and any generated errors. | |
48 | crate struct NllOutput<'tcx> { | |
49 | pub regioncx: RegionInferenceContext<'tcx>, | |
74b04a01 | 50 | pub opaque_type_values: FxHashMap<DefId, ty::ResolvedOpaqueTy<'tcx>>, |
dfeec247 XL |
51 | pub polonius_output: Option<Rc<PoloniusOutput>>, |
52 | pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>, | |
53 | pub nll_errors: RegionErrors<'tcx>, | |
54 | } | |
55 | ||
56 | /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal | |
57 | /// regions (e.g., region parameters) declared on the function. That set will need to be given to | |
ff7c6d11 | 58 | /// `compute_regions`. |
dc9dc135 XL |
59 | pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( |
60 | infcx: &InferCtxt<'cx, 'tcx>, | |
ff7c6d11 XL |
61 | def_id: DefId, |
62 | param_env: ty::ParamEnv<'tcx>, | |
60c5eb7d XL |
63 | body: &mut BodyAndCache<'tcx>, |
64 | promoted: &mut IndexVec<Promoted, BodyAndCache<'tcx>>, | |
ff7c6d11 XL |
65 | ) -> UniversalRegions<'tcx> { |
66 | debug!("replace_regions_in_mir(def_id={:?})", def_id); | |
67 | ||
68 | // Compute named region information. This also renumbers the inputs/outputs. | |
69 | let universal_regions = UniversalRegions::new(infcx, def_id, param_env); | |
70 | ||
71 | // Replace all remaining regions with fresh inference variables. | |
e1599b0c | 72 | renumber::renumber_mir(infcx, body, promoted); |
ff7c6d11 XL |
73 | |
74 | let source = MirSource::item(def_id); | |
dc9dc135 | 75 | mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, body, |_, _| Ok(())); |
ff7c6d11 XL |
76 | |
77 | universal_regions | |
78 | } | |
79 | ||
e1599b0c XL |
80 | // This function populates an AllFacts instance with base facts related to |
81 | // MovePaths and needed for the move analysis. | |
82 | fn populate_polonius_move_facts( | |
83 | all_facts: &mut AllFacts, | |
84 | move_data: &MoveData<'_>, | |
85 | location_table: &LocationTable, | |
dfeec247 XL |
86 | body: &Body<'_>, |
87 | ) { | |
e1599b0c | 88 | all_facts |
74b04a01 | 89 | .path_is_var |
dfeec247 | 90 | .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(v, &m)| (m, v))); |
e1599b0c XL |
91 | |
92 | for (child, move_path) in move_data.move_paths.iter_enumerated() { | |
74b04a01 XL |
93 | if let Some(parent) = move_path.parent { |
94 | all_facts.child_path.push((child, parent)); | |
95 | } | |
e1599b0c XL |
96 | } |
97 | ||
74b04a01 XL |
98 | let fn_entry_start = location_table |
99 | .start_index(Location { block: BasicBlock::from_u32(0u32), statement_index: 0 }); | |
100 | ||
e1599b0c XL |
101 | // initialized_at |
102 | for init in move_data.inits.iter() { | |
e1599b0c XL |
103 | match init.location { |
104 | InitLocation::Statement(location) => { | |
105 | let block_data = &body[location.block]; | |
106 | let is_terminator = location.statement_index == block_data.statements.len(); | |
107 | ||
108 | if is_terminator && init.kind == InitKind::NonPanicPathOnly { | |
109 | // We are at the terminator of an init that has a panic path, | |
110 | // and where the init should not happen on panic | |
111 | ||
112 | for &successor in block_data.terminator().successors() { | |
113 | if body[successor].is_cleanup { | |
114 | continue; | |
115 | } | |
116 | ||
117 | // The initialization happened in (or rather, when arriving at) | |
118 | // the successors, but not in the unwind block. | |
dfeec247 | 119 | let first_statement = Location { block: successor, statement_index: 0 }; |
e1599b0c | 120 | all_facts |
74b04a01 | 121 | .path_assigned_at_base |
e1599b0c XL |
122 | .push((init.path, location_table.start_index(first_statement))); |
123 | } | |
e1599b0c XL |
124 | } else { |
125 | // In all other cases, the initialization just happens at the | |
126 | // midpoint, like any other effect. | |
74b04a01 XL |
127 | all_facts |
128 | .path_assigned_at_base | |
129 | .push((init.path, location_table.mid_index(location))); | |
e1599b0c | 130 | } |
dfeec247 | 131 | } |
e1599b0c XL |
132 | // Arguments are initialized on function entry |
133 | InitLocation::Argument(local) => { | |
134 | assert!(body.local_kind(local) == LocalKind::Arg); | |
74b04a01 | 135 | all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); |
e1599b0c XL |
136 | } |
137 | } | |
138 | } | |
139 | ||
74b04a01 XL |
140 | for (local, &path) in move_data.rev_lookup.iter_locals_enumerated() { |
141 | if body.local_kind(local) != LocalKind::Arg { | |
142 | // Non-arguments start out deinitialised; we simulate this with an | |
143 | // initial move: | |
144 | all_facts.path_moved_at_base.push((path, fn_entry_start)); | |
145 | } | |
146 | } | |
147 | ||
e1599b0c XL |
148 | // moved_out_at |
149 | // deinitialisation is assumed to always happen! | |
150 | all_facts | |
74b04a01 | 151 | .path_moved_at_base |
dfeec247 | 152 | .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); |
e1599b0c XL |
153 | } |
154 | ||
ff7c6d11 XL |
155 | /// Computes the (non-lexical) regions from the input MIR. |
156 | /// | |
157 | /// This may result in errors being reported. | |
dc9dc135 XL |
158 | pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( |
159 | infcx: &InferCtxt<'cx, 'tcx>, | |
ff7c6d11 XL |
160 | def_id: DefId, |
161 | universal_regions: UniversalRegions<'tcx>, | |
60c5eb7d XL |
162 | body: ReadOnlyBodyAndCache<'_, 'tcx>, |
163 | promoted: &IndexVec<Promoted, ReadOnlyBodyAndCache<'_, 'tcx>>, | |
83c7162d | 164 | location_table: &LocationTable, |
dc9dc135 | 165 | param_env: ty::ParamEnv<'tcx>, |
74b04a01 | 166 | flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, |
ff7c6d11 | 167 | move_data: &MoveData<'tcx>, |
83c7162d | 168 | borrow_set: &BorrowSet<'tcx>, |
dfeec247 | 169 | ) -> NllOutput<'tcx> { |
60c5eb7d | 170 | let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); |
94b46f34 | 171 | |
8faf50e0 XL |
172 | let universal_regions = Rc::new(universal_regions); |
173 | ||
dfeec247 | 174 | let elements = &Rc::new(RegionValueElements::new(&body)); |
8faf50e0 | 175 | |
ff7c6d11 | 176 | // Run the MIR type-checker. |
74b04a01 XL |
177 | let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } = |
178 | type_check::type_check( | |
179 | infcx, | |
180 | param_env, | |
181 | body, | |
182 | promoted, | |
183 | def_id, | |
184 | &universal_regions, | |
185 | location_table, | |
186 | borrow_set, | |
187 | &mut all_facts, | |
188 | flow_inits, | |
189 | move_data, | |
190 | elements, | |
191 | ); | |
ff7c6d11 | 192 | |
83c7162d | 193 | if let Some(all_facts) = &mut all_facts { |
60c5eb7d | 194 | let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation"); |
dfeec247 | 195 | all_facts.universal_region.extend(universal_regions.universal_regions()); |
60c5eb7d XL |
196 | populate_polonius_move_facts(all_facts, move_data, location_table, &body); |
197 | ||
198 | // Emit universal regions facts, and their relations, for Polonius. | |
199 | // | |
200 | // 1: universal regions are modeled in Polonius as a pair: | |
201 | // - the universal region vid itself. | |
202 | // - a "placeholder loan" associated to this universal region. Since they don't exist in | |
203 | // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index | |
204 | // added to the existing number of loans, as if they succeeded them in the set. | |
205 | // | |
206 | let borrow_count = borrow_set.borrows.len(); | |
207 | debug!( | |
208 | "compute_regions: polonius placeholders, num_universals={}, borrow_count={}", | |
209 | universal_regions.len(), | |
210 | borrow_count | |
211 | ); | |
212 | ||
213 | for universal_region in universal_regions.universal_regions() { | |
214 | let universal_region_idx = universal_region.index(); | |
215 | let placeholder_loan_idx = borrow_count + universal_region_idx; | |
216 | all_facts.placeholder.push((universal_region, placeholder_loan_idx.into())); | |
217 | } | |
218 | ||
219 | // 2: the universal region relations `outlives` constraints are emitted as | |
220 | // `known_subset` facts. | |
221 | for (fr1, fr2) in universal_region_relations.known_outlives() { | |
222 | if fr1 != fr2 { | |
223 | debug!( | |
224 | "compute_regions: emitting polonius `known_subset` fr1={:?}, fr2={:?}", | |
225 | fr1, fr2 | |
226 | ); | |
227 | all_facts.known_subset.push((*fr1, *fr2)); | |
228 | } | |
229 | } | |
83c7162d XL |
230 | } |
231 | ||
94b46f34 XL |
232 | // Create the region inference context, taking ownership of the |
233 | // region inference data that was contained in `infcx`, and the | |
234 | // base constraints generated by the type-check. | |
ff7c6d11 | 235 | let var_origins = infcx.take_region_var_origins(); |
94b46f34 | 236 | let MirTypeckRegionConstraints { |
a1dfa0c6 XL |
237 | placeholder_indices, |
238 | placeholder_index_to_region: _, | |
8faf50e0 | 239 | mut liveness_constraints, |
94b46f34 | 240 | outlives_constraints, |
dc9dc135 | 241 | member_constraints, |
0bf4aa26 | 242 | closure_bounds_mapping, |
94b46f34 | 243 | type_tests, |
b7449926 | 244 | } = constraints; |
a1dfa0c6 | 245 | let placeholder_indices = Rc::new(placeholder_indices); |
8faf50e0 XL |
246 | |
247 | constraint_generation::generate_constraints( | |
248 | infcx, | |
249 | &mut liveness_constraints, | |
250 | &mut all_facts, | |
251 | location_table, | |
dc9dc135 | 252 | &body, |
8faf50e0 XL |
253 | borrow_set, |
254 | ); | |
255 | ||
94b46f34 XL |
256 | let mut regioncx = RegionInferenceContext::new( |
257 | var_origins, | |
258 | universal_regions, | |
0bf4aa26 | 259 | placeholder_indices, |
8faf50e0 | 260 | universal_region_relations, |
94b46f34 | 261 | outlives_constraints, |
dc9dc135 | 262 | member_constraints, |
0bf4aa26 | 263 | closure_bounds_mapping, |
94b46f34 | 264 | type_tests, |
8faf50e0 XL |
265 | liveness_constraints, |
266 | elements, | |
94b46f34 | 267 | ); |
ff7c6d11 | 268 | |
94b46f34 | 269 | // Generate various additional constraints. |
dfeec247 | 270 | invalidation::generate_invalidates(infcx.tcx, &mut all_facts, location_table, body, borrow_set); |
ff7c6d11 | 271 | |
83c7162d | 272 | // Dump facts if requested. |
94b46f34 XL |
273 | let polonius_output = all_facts.and_then(|all_facts| { |
274 | if infcx.tcx.sess.opts.debugging_opts.nll_facts { | |
ba9703b0 | 275 | let def_path = infcx.tcx.def_path(def_id); |
94b46f34 XL |
276 | let dir_path = |
277 | PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate()); | |
278 | all_facts.write_to_dir(dir_path, location_table).unwrap(); | |
279 | } | |
280 | ||
281 | if infcx.tcx.sess.opts.debugging_opts.polonius { | |
dfeec247 XL |
282 | let algorithm = |
283 | env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Naive")); | |
94b46f34 XL |
284 | let algorithm = Algorithm::from_str(&algorithm).unwrap(); |
285 | debug!("compute_regions: using polonius algorithm {:?}", algorithm); | |
60c5eb7d | 286 | let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); |
dfeec247 | 287 | Some(Rc::new(Output::compute(&all_facts, algorithm, false))) |
94b46f34 XL |
288 | } else { |
289 | None | |
290 | } | |
291 | }); | |
ff7c6d11 XL |
292 | |
293 | // Solve the region constraints. | |
dfeec247 XL |
294 | let (closure_region_requirements, nll_errors) = |
295 | regioncx.solve(infcx, &body, def_id, polonius_output.clone()); | |
296 | ||
74b04a01 XL |
297 | if !nll_errors.is_empty() { |
298 | // Suppress unhelpful extra errors in `infer_opaque_types`. | |
299 | infcx.set_tainted_by_errors(); | |
300 | } | |
301 | ||
302 | let remapped_opaque_tys = regioncx.infer_opaque_types(&infcx, opaque_type_values, body.span); | |
303 | ||
dfeec247 XL |
304 | NllOutput { |
305 | regioncx, | |
74b04a01 | 306 | opaque_type_values: remapped_opaque_tys, |
dfeec247 XL |
307 | polonius_output, |
308 | opt_closure_req: closure_region_requirements, | |
309 | nll_errors, | |
310 | } | |
ff7c6d11 XL |
311 | } |
312 | ||
dfeec247 | 313 | pub(super) fn dump_mir_results<'a, 'tcx>( |
dc9dc135 | 314 | infcx: &InferCtxt<'a, 'tcx>, |
9fa01778 | 315 | source: MirSource<'tcx>, |
dc9dc135 | 316 | body: &Body<'tcx>, |
9fa01778 XL |
317 | regioncx: &RegionInferenceContext<'_>, |
318 | closure_region_requirements: &Option<ClosureRegionRequirements<'_>>, | |
ff7c6d11 | 319 | ) { |
ba9703b0 | 320 | if !mir_util::dump_enabled(infcx.tcx, "nll", source.def_id()) { |
ff7c6d11 XL |
321 | return; |
322 | } | |
323 | ||
dfeec247 XL |
324 | mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, body, |pass_where, out| { |
325 | match pass_where { | |
326 | // Before the CFG, dump out the values for each region variable. | |
327 | PassWhere::BeforeCFG => { | |
328 | regioncx.dump_mir(out)?; | |
329 | writeln!(out, "|")?; | |
330 | ||
331 | if let Some(closure_region_requirements) = closure_region_requirements { | |
332 | writeln!(out, "| Free Region Constraints")?; | |
333 | for_each_region_constraint(closure_region_requirements, &mut |msg| { | |
334 | writeln!(out, "| {}", msg) | |
335 | })?; | |
0731742a | 336 | writeln!(out, "|")?; |
ff7c6d11 | 337 | } |
dfeec247 | 338 | } |
ff7c6d11 | 339 | |
dfeec247 | 340 | PassWhere::BeforeLocation(_) => {} |
ff7c6d11 | 341 | |
dfeec247 | 342 | PassWhere::AfterTerminator(_) => {} |
8faf50e0 | 343 | |
dfeec247 XL |
344 | PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} |
345 | } | |
346 | Ok(()) | |
347 | }); | |
ff7c6d11 XL |
348 | |
349 | // Also dump the inference graph constraints as a graphviz file. | |
9fa01778 | 350 | let _: io::Result<()> = try { |
ff7c6d11 | 351 | let mut file = |
8faf50e0 XL |
352 | pretty::create_dump_file(infcx.tcx, "regioncx.all.dot", None, "nll", &0, source)?; |
353 | regioncx.dump_graphviz_raw_constraints(&mut file)?; | |
354 | }; | |
355 | ||
356 | // Also dump the inference graph constraints as a graphviz file. | |
9fa01778 | 357 | let _: io::Result<()> = try { |
8faf50e0 XL |
358 | let mut file = |
359 | pretty::create_dump_file(infcx.tcx, "regioncx.scc.dot", None, "nll", &0, source)?; | |
360 | regioncx.dump_graphviz_scc_constraints(&mut file)?; | |
94b46f34 | 361 | }; |
ff7c6d11 XL |
362 | } |
363 | ||
dfeec247 | 364 | pub(super) fn dump_annotation<'a, 'tcx>( |
dc9dc135 XL |
365 | infcx: &InferCtxt<'a, 'tcx>, |
366 | body: &Body<'tcx>, | |
ff7c6d11 | 367 | mir_def_id: DefId, |
b7449926 | 368 | regioncx: &RegionInferenceContext<'tcx>, |
9fa01778 | 369 | closure_region_requirements: &Option<ClosureRegionRequirements<'_>>, |
74b04a01 | 370 | opaque_type_values: &FxHashMap<DefId, ty::ResolvedOpaqueTy<'tcx>>, |
8faf50e0 | 371 | errors_buffer: &mut Vec<Diagnostic>, |
ff7c6d11 XL |
372 | ) { |
373 | let tcx = infcx.tcx; | |
374 | let base_def_id = tcx.closure_base_def_id(mir_def_id); | |
48663c56 | 375 | if !tcx.has_attr(base_def_id, sym::rustc_regions) { |
ff7c6d11 XL |
376 | return; |
377 | } | |
378 | ||
379 | // When the enclosing function is tagged with `#[rustc_regions]`, | |
380 | // we dump out various bits of state as warnings. This is useful | |
381 | // for verifying that the compiler is behaving as expected. These | |
382 | // warnings focus on the closure region requirements -- for | |
383 | // viewing the intraprocedural state, the -Zdump-mir output is | |
384 | // better. | |
385 | ||
74b04a01 | 386 | let mut err = if let Some(closure_region_requirements) = closure_region_requirements { |
dfeec247 | 387 | let mut err = tcx.sess.diagnostic().span_note_diag(body.span, "external requirements"); |
ff7c6d11 | 388 | |
b7449926 | 389 | regioncx.annotate(tcx, &mut err); |
ff7c6d11 XL |
390 | |
391 | err.note(&format!( | |
392 | "number of external vids: {}", | |
393 | closure_region_requirements.num_external_vids | |
394 | )); | |
395 | ||
396 | // Dump the region constraints we are imposing *between* those | |
397 | // newly created variables. | |
398 | for_each_region_constraint(closure_region_requirements, &mut |msg| { | |
399 | err.note(msg); | |
400 | Ok(()) | |
dfeec247 XL |
401 | }) |
402 | .unwrap(); | |
ff7c6d11 | 403 | |
74b04a01 | 404 | err |
ff7c6d11 | 405 | } else { |
dfeec247 | 406 | let mut err = tcx.sess.diagnostic().span_note_diag(body.span, "no external requirements"); |
b7449926 | 407 | regioncx.annotate(tcx, &mut err); |
8faf50e0 | 408 | |
74b04a01 XL |
409 | err |
410 | }; | |
411 | ||
412 | if !opaque_type_values.is_empty() { | |
413 | err.note(&format!("Inferred opaque type values:\n{:#?}", opaque_type_values)); | |
ff7c6d11 | 414 | } |
74b04a01 XL |
415 | |
416 | err.buffer(errors_buffer); | |
ff7c6d11 XL |
417 | } |
418 | ||
419 | fn for_each_region_constraint( | |
9fa01778 | 420 | closure_region_requirements: &ClosureRegionRequirements<'_>, |
0531ce1d | 421 | with_msg: &mut dyn FnMut(&str) -> io::Result<()>, |
ff7c6d11 XL |
422 | ) -> io::Result<()> { |
423 | for req in &closure_region_requirements.outlives_requirements { | |
0531ce1d | 424 | let subject: &dyn Debug = match &req.subject { |
ff7c6d11 XL |
425 | ClosureOutlivesSubject::Region(subject) => subject, |
426 | ClosureOutlivesSubject::Ty(ty) => ty, | |
427 | }; | |
dfeec247 | 428 | with_msg(&format!("where {:?}: {:?}", subject, req.outlived_free_region,))?; |
ff7c6d11 XL |
429 | } |
430 | Ok(()) | |
431 | } | |
432 | ||
433 | /// Right now, we piggy back on the `ReVar` to store our NLL inference | |
434 | /// regions. These are indexed with `RegionVid`. This method will | |
0531ce1d | 435 | /// assert that the region is a `ReVar` and extract its internal index. |
ff7c6d11 XL |
436 | /// This is reasonable because in our MIR we replace all universal regions |
437 | /// with inference variables. | |
438 | pub trait ToRegionVid { | |
439 | fn to_region_vid(self) -> RegionVid; | |
440 | } | |
441 | ||
442 | impl<'tcx> ToRegionVid for &'tcx RegionKind { | |
443 | fn to_region_vid(self) -> RegionVid { | |
dfeec247 | 444 | if let ty::ReVar(vid) = self { *vid } else { bug!("region is not an ReVar: {:?}", self) } |
ff7c6d11 XL |
445 | } |
446 | } | |
447 | ||
448 | impl ToRegionVid for RegionVid { | |
449 | fn to_region_vid(self) -> RegionVid { | |
450 | self | |
451 | } | |
452 | } | |
0bf4aa26 XL |
453 | |
454 | crate trait ConstraintDescription { | |
455 | fn description(&self) -> &'static str; | |
456 | } |