diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 63d5de4f2e5..c236fbc4f72 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -104,6 +104,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( None }; + let universal_regions = Rc::new(universal_regions); + let elements = &Rc::new(RegionValueElements::new(mir)); // Run the MIR type-checker. diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 19ff6d9a7e3..9785a544a4d 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -206,15 +206,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// of constraints produced by the MIR type check. pub(crate) fn new( var_infos: VarInfos, - universal_regions: UniversalRegions<'tcx>, + universal_regions: Rc>, _mir: &Mir<'tcx>, outlives_constraints: ConstraintSet, type_tests: Vec>, liveness_constraints: LivenessValues, elements: &Rc, ) -> Self { - let universal_regions = Rc::new(universal_regions); - // Create a RegionDefinition for each inference variable. let definitions: IndexVec<_, _> = var_infos .into_iter() diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs new file mode 100644 index 00000000000..3cf3ae1d166 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -0,0 +1,225 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use borrow_check::location::LocationTable; +use borrow_check::nll::facts::AllFacts; +use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::type_check::constraint_conversion; +use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; +use rustc::hir::def_id::DefId; +use rustc::infer::region_constraints::GenericKind; +use rustc::infer::InferCtxt; +use rustc::traits::query::outlives_bounds::{self, OutlivesBound}; +use rustc::traits::query::type_op::{self, TypeOp}; +use rustc::ty::{self, RegionVid, Ty}; +use rustc_data_structures::transitive_relation::TransitiveRelation; +use std::rc::Rc; +use syntax::ast; + +#[derive(Debug)] +crate struct UniversalRegionRelations<'tcx> { + universal_regions: Rc>, + + /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to + /// be true. These encode relationships like `T: 'a` that are + /// added via implicit bounds. + /// + /// Each region here is guaranteed to be a key in the `indices` + /// map. We use the "original" regions (i.e., the keys from the + /// map, and not the values) because the code in + /// `process_registered_region_obligations` has some special-cased + /// logic expecting to see (e.g.) `ReStatic`, and if we supplied + /// our special inference variable there, we would mess that up. + crate region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, + + /// Stores the outlives relations that are known to hold from the + /// implied bounds, in-scope where clauses, and that sort of + /// thing. + outlives: TransitiveRelation, + + /// This is the `<=` relation; that is, if `a: b`, then `b <= a`, + /// and we store that here. This is useful when figuring out how + /// to express some local region in terms of external regions our + /// caller will understand. + inverse_outlives: TransitiveRelation, +} + +impl UniversalRegionRelations<'tcx> { + crate fn create( + infcx: &InferCtxt<'_, '_, 'tcx>, + mir_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + location_table: &LocationTable, + implicit_region_bound: Option>, + universal_regions: &Rc>, + constraints: &mut MirTypeckRegionConstraints<'tcx>, + all_facts: &mut Option, + ) -> Self { + let mir_node_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap(); + UniversalRegionRelationsBuilder { + infcx, + mir_def_id, + mir_node_id, + param_env, + implicit_region_bound, + constraints, + location_table, + all_facts, + universal_regions: universal_regions.clone(), + relations: UniversalRegionRelations { + universal_regions: universal_regions.clone(), + region_bound_pairs: Vec::new(), + outlives: TransitiveRelation::new(), + inverse_outlives: TransitiveRelation::new(), + }, + }.create() + } + + /// Records in the `outlives_relation` (and + /// `inverse_outlives_relation`) that `fr_a: fr_b`. Invoked by the + /// builder below. + fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) { + debug!( + "relate_universal_regions: fr_a={:?} outlives fr_b={:?}", + fr_a, fr_b + ); + self.outlives.add(fr_a, fr_b); + self.inverse_outlives.add(fr_b, fr_a); + } +} + +struct UniversalRegionRelationsBuilder<'this, 'gcx: 'tcx, 'tcx: 'this> { + infcx: &'this InferCtxt<'this, 'gcx, 'tcx>, + mir_def_id: DefId, + mir_node_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + location_table: &'this LocationTable, + universal_regions: Rc>, + relations: UniversalRegionRelations<'tcx>, + implicit_region_bound: Option>, + constraints: &'this mut MirTypeckRegionConstraints<'tcx>, + all_facts: &'this mut Option, +} + +impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> { + crate fn create(mut self) -> UniversalRegionRelations<'tcx> { + let unnormalized_input_output_tys = self + .universal_regions + .unnormalized_input_tys + .iter() + .cloned() + .chain(Some(self.universal_regions.unnormalized_output_ty)); + + // For each of the input/output types: + // - Normalize the type. This will create some region + // constraints, which we buffer up because we are + // not ready to process them yet. + // - Then compute the implied bounds. This will adjust + // the `relations.region_bound_pairs` and so forth. + // - After this is done, we'll process the constraints, once + // the `relations` is built. + let constraint_sets: Vec<_> = unnormalized_input_output_tys + .flat_map(|ty| { + debug!("build: input_or_output={:?}", ty); + let (ty, constraints) = self + .param_env + .and(type_op::normalize::Normalize::new(ty)) + .fully_perform(self.infcx) + .unwrap_or_else(|_| bug!("failed to normalize {:?}", ty)); + self.add_implied_bounds(ty); + constraints + }) + .collect(); + + // Insert the facts we know from the predicates. Why? Why not. + let param_env = self.param_env; + self.add_outlives_bounds(outlives_bounds::explicit_outlives_bounds(param_env)); + + // Finally: + // - outlives is reflexive, so `'r: 'r` for every region `'r` + // - `'static: 'r` for every region `'r` + // - `'r: 'fn_body` for every (other) universally quantified + // region `'r`, all of which are provided by our caller + let fr_static = self.universal_regions.fr_static; + let fr_fn_body = self.universal_regions.fr_fn_body; + for fr in self.universal_regions.universal_regions() { + debug!( + "build: relating free region {:?} to itself and to 'static", + fr + ); + self.relations.relate_universal_regions(fr, fr); + self.relations.relate_universal_regions(fr_static, fr); + self.relations.relate_universal_regions(fr, fr_fn_body); + } + + for data in constraint_sets { + constraint_conversion::ConstraintConversion::new( + self.infcx.tcx, + &self.universal_regions, + &self.location_table, + &self.relations.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + Locations::All, + &mut self.constraints.outlives_constraints, + &mut self.constraints.type_tests, + &mut self.all_facts, + ).convert_all(&data); + } + + self.relations + } + + /// Update the type of a single local, which should represent + /// either the return type of the MIR or one of its arguments. At + /// the same time, compute and add any implied bounds that come + /// from this local. + fn add_implied_bounds(&mut self, ty: Ty<'tcx>) { + debug!("add_implied_bounds(ty={:?})", ty); + let span = self.infcx.tcx.def_span(self.mir_def_id); + let bounds = self + .infcx + .implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span); + self.add_outlives_bounds(bounds); + } + + /// Registers the `OutlivesBound` items from `outlives_bounds` in + /// the outlives relation as well as the region-bound pairs + /// listing. + fn add_outlives_bounds(&mut self, outlives_bounds: I) + where + I: IntoIterator>, + { + for outlives_bound in outlives_bounds { + debug!("add_outlives_bounds(bound={:?})", outlives_bound); + + match outlives_bound { + OutlivesBound::RegionSubRegion(r1, r2) => { + // The bound says that `r1 <= r2`; we store `r2: r1`. + let r1 = self.universal_regions.to_region_vid(r1); + let r2 = self.universal_regions.to_region_vid(r2); + self.relations.relate_universal_regions(r2, r1); + } + + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.relations + .region_bound_pairs + .push((r_a, GenericKind::Param(param_b))); + } + + OutlivesBound::RegionSubProjection(r_a, projection_b) => { + self.relations + .region_bound_pairs + .push((r_a, GenericKind::Projection(projection_b))); + } + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index b67de34593f..be14819b648 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -71,6 +71,7 @@ macro_rules! span_mirbug_and_err { } mod constraint_conversion; +mod free_region_relations; mod input_output; mod liveness; mod relate_tys; @@ -110,7 +111,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, mir_def_id: DefId, - universal_regions: &UniversalRegions<'tcx>, + universal_regions: &Rc>, location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, liveness: &LivenessResults, @@ -127,6 +128,17 @@ pub(crate) fn type_check<'gcx, 'tcx>( type_tests: Vec::default(), }; + let _urr = free_region_relations::UniversalRegionRelations::create( + infcx, + mir_def_id, + param_env, + location_table, + Some(implicit_region_bound), + universal_regions, + &mut constraints, + all_facts, + ); + { let mut borrowck_context = BorrowCheckContext { universal_regions, diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 54730ecd75f..3f32d307409 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -14,6 +14,8 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! */ +#![feature(infer_outlives_requirements)] +#![feature(in_band_lifetimes)] #![feature(slice_patterns)] #![feature(slice_sort_by_cached_key)] #![feature(from_ref)]