From 018c515f077597ad45f85d962a49ab6fbc5421ac Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 28 Sep 2018 16:55:15 -0400 Subject: [PATCH 01/11] move `PlaceholderIndices` into `MirTypeckRegionConstraints` struct --- src/librustc_mir/borrow_check/nll/mod.rs | 5 ++--- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 8 +++----- .../borrow_check/nll/type_check/relate_tys.rs | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 8fc54b6ff92..03cdb5f4cb8 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -107,7 +107,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // Run the MIR type-checker. let MirTypeckResults { constraints, - placeholder_indices, universal_region_relations, } = type_check::type_check( infcx, @@ -123,8 +122,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( elements, ); - let placeholder_indices = Rc::new(placeholder_indices); - if let Some(all_facts) = &mut all_facts { all_facts .universal_region @@ -136,11 +133,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // base constraints generated by the type-check. let var_origins = infcx.take_region_var_origins(); let MirTypeckRegionConstraints { + placeholder_indices, mut liveness_constraints, outlives_constraints, closure_bounds_mapping, type_tests, } = constraints; + let placeholder_indices = Rc::new(placeholder_indices); constraint_generation::generate_constraints( infcx, 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 953fe0c9521..bcf73c5c644 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -126,12 +126,12 @@ pub(crate) fn type_check<'gcx, 'tcx>( ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { + placeholder_indices: PlaceholderIndices::default(), liveness_constraints: LivenessValues::new(elements), outlives_constraints: ConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), }; - let mut placeholder_indices = PlaceholderIndices::default(); let CreateResult { universal_region_relations, @@ -151,7 +151,6 @@ pub(crate) fn type_check<'gcx, 'tcx>( borrow_set, all_facts, constraints: &mut constraints, - placeholder_indices: &mut placeholder_indices, }; type_check_internal( @@ -175,7 +174,6 @@ pub(crate) fn type_check<'gcx, 'tcx>( MirTypeckResults { constraints, - placeholder_indices, universal_region_relations, } } @@ -730,18 +728,18 @@ struct BorrowCheckContext<'a, 'tcx: 'a> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - placeholder_indices: &'a mut PlaceholderIndices, } crate struct MirTypeckResults<'tcx> { crate constraints: MirTypeckRegionConstraints<'tcx>, - crate placeholder_indices: PlaceholderIndices, crate universal_region_relations: Rc>, } /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. crate struct MirTypeckRegionConstraints<'tcx> { + crate placeholder_indices: PlaceholderIndices, + /// In general, the type-checker is not responsible for enforcing /// liveness constraints; this job falls to the region inferencer, /// which performs a liveness analysis. However, in some limited diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index 13ebf46bdb1..5c699b5295d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -83,7 +83,7 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { fn next_placeholder_region(&mut self, placeholder: ty::Placeholder) -> ty::Region<'tcx> { let origin = NLLRegionVariableOrigin::Placeholder(placeholder); if let Some(borrowck_context) = &mut self.borrowck_context { - borrowck_context.placeholder_indices.insert(placeholder); + borrowck_context.constraints.placeholder_indices.insert(placeholder); } self.infcx.next_nll_region_var(origin) } From 784746f57e6e9d76f4ecc0415e8ab926a81ce37e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Oct 2018 10:13:13 -0400 Subject: [PATCH 02/11] convert placeholder region vids in NLL mode as we see them --- src/librustc_mir/borrow_check/nll/mod.rs | 1 + .../nll/region_infer/error_reporting/mod.rs | 1 + .../borrow_check/nll/region_infer/mod.rs | 5 ++ .../nll/type_check/constraint_conversion.rs | 54 +++++++++++-------- .../nll/type_check/free_region_relations.rs | 5 +- .../borrow_check/nll/type_check/mod.rs | 46 +++++++++++++--- .../borrow_check/nll/type_check/relate_tys.rs | 14 +++-- 7 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 03cdb5f4cb8..0c4140caee8 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -134,6 +134,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( let var_origins = infcx.take_region_var_origins(); let MirTypeckRegionConstraints { placeholder_indices, + placeholder_index_to_region: _, mut liveness_constraints, outlives_constraints, closure_bounds_mapping, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index ccb44c670f7..64135195637 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -594,6 +594,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Finds some region R such that `fr1: R` and `R` is live at // `elem`. crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { + debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); // Find all paths let (_path, r) = self.find_constraint_paths_between_regions(fr1, |r| { 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 50fd4afcd7e..0e44c8b36a9 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1226,6 +1226,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); let longer_fr_scc = self.constraint_sccs.scc(longer_fr); + debug!( + "check_bound_universal_region: longer_fr_scc={:?}", + longer_fr_scc, + ); // If we have some bound universal region `'a`, then the only // elements it can contain is itself -- we don't know anything @@ -1242,6 +1246,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { Some(v) => v, None => return, }; + debug!("check_bound_universal_region: error_element = {:?}", error_element); // Find the region that introduced this `error_element`. let error_region = match error_element { diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 994f20a011d..5904138ef60 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -8,21 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; +use borrow_check::nll::constraints::OutlivesConstraint; use borrow_check::nll::region_infer::TypeTest; -use borrow_check::nll::type_check::Locations; +use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::ToRegionVid; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc::infer::region_constraints::{GenericKind, VerifyBound}; -use rustc::infer::{self, SubregionOrigin}; +use rustc::infer::{self, InferCtxt, SubregionOrigin}; use rustc::mir::ConstraintCategory; use rustc::ty::subst::UnpackedKind; use rustc::ty::{self, TyCtxt}; use syntax_pos::DUMMY_SP; crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, @@ -30,32 +32,30 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'tcx>, locations: Locations, category: ConstraintCategory, - outlives_constraints: &'a mut ConstraintSet, - type_tests: &'a mut Vec>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, } impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { crate fn new( - tcx: TyCtxt<'a, 'gcx, 'tcx>, + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, locations: Locations, category: ConstraintCategory, - outlives_constraints: &'a mut ConstraintSet, - type_tests: &'a mut Vec>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, ) -> Self { Self { - tcx, + infcx, + tcx: infcx.tcx, universal_regions, region_bound_pairs, implicit_region_bound, param_env, locations, category, - outlives_constraints, - type_tests, + constraints, } } @@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } fn verify_to_type_test( - &self, + &mut self, generic_kind: GenericKind<'tcx>, region: ty::Region<'tcx>, verify_bound: VerifyBound<'tcx>, @@ -128,22 +128,30 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } } - fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { - self.universal_regions.to_region_vid(r) + fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid { + if let ty::RePlaceholder(placeholder) = r { + self.constraints + .placeholder_region(self.infcx, *placeholder) + .to_region_vid() + } else { + self.universal_regions.to_region_vid(r) + } } fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) { - self.outlives_constraints.push(OutlivesConstraint { - locations: self.locations, - category: self.category, - sub, - sup, - }); + self.constraints + .outlives_constraints + .push(OutlivesConstraint { + locations: self.locations, + category: self.category, + sub, + sup, + }); } fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { debug!("add_type_test(type_test={:?})", type_test); - self.type_tests.push(type_test); + self.constraints.type_tests.push(type_test); } } @@ -156,8 +164,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) { - let b = self.universal_regions.to_region_vid(b); - let a = self.universal_regions.to_region_vid(a); + let b = self.to_region_vid(b); + let a = self.to_region_vid(a); self.add_outlives(b, a); } 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 index f8c839e4d3f..3d0f3d9fc7d 100644 --- 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 @@ -271,15 +271,14 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> { for data in constraint_sets { constraint_conversion::ConstraintConversion::new( - self.infcx.tcx, + self.infcx, &self.universal_regions, &self.region_bound_pairs, self.implicit_region_bound, self.param_env, Locations::All(DUMMY_SP), ConstraintCategory::Internal, - &mut self.constraints.outlives_constraints, - &mut self.constraints.type_tests, + &mut self.constraints, ).convert_all(&data); } 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 bcf73c5c644..eae41c81ff2 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -16,6 +16,7 @@ use borrow_check::location::LocationTable; use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::values::LivenessValues; +use borrow_check::nll::region_infer::values::PlaceholderIndex; use borrow_check::nll::region_infer::values::PlaceholderIndices; use borrow_check::nll::region_infer::values::RegionValueElements; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; @@ -28,11 +29,12 @@ use borrow_check::nll::ToRegionVid; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; +use either::Either; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::outlives::env::RegionBoundPairs; -use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; +use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; @@ -44,14 +46,13 @@ use rustc::traits::{ObligationCause, PredicateObligations}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::indexed_vec::IndexVec; use std::rc::Rc; use std::{fmt, iter}; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; -use either::Either; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ $crate::borrow_check::nll::type_check::mirbug( @@ -127,6 +128,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { placeholder_indices: PlaceholderIndices::default(), + placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::new(elements), outlives_constraints: ConstraintSet::default(), closure_bounds_mapping: Default::default(), @@ -738,8 +740,20 @@ crate struct MirTypeckResults<'tcx> { /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. crate struct MirTypeckRegionConstraints<'tcx> { + /// Maps from a `ty::Placeholder` to the corresponding + /// `PlaceholderIndex` bit that we will use for it. + /// + /// To keep everything in sync, do not insert this set + /// directly. Instead, use the `placeholder_region` helper. crate placeholder_indices: PlaceholderIndices, + /// Each time we add a placeholder to `placeholder_indices`, we + /// also create a corresponding "representative" region vid for + /// that wraps it. This vector tracks those. This way, when we + /// convert the same `ty::RePlaceholder(p)` twice, we can map to + /// the same underlying `RegionVid`. + crate placeholder_index_to_region: IndexVec>, + /// In general, the type-checker is not responsible for enforcing /// liveness constraints; this job falls to the region inferencer, /// which performs a liveness analysis. However, in some limited @@ -757,6 +771,25 @@ crate struct MirTypeckRegionConstraints<'tcx> { crate type_tests: Vec>, } +impl MirTypeckRegionConstraints<'tcx> { + fn placeholder_region( + &mut self, + infcx: &InferCtxt<'_, '_, 'tcx>, + placeholder: ty::Placeholder, + ) -> ty::Region<'tcx> { + let placeholder_index = self.placeholder_indices.insert(placeholder); + match self.placeholder_index_to_region.get(placeholder_index) { + Some(&v) => v, + None => { + let origin = NLLRegionVariableOrigin::Placeholder(placeholder); + let region = infcx.next_nll_region_var(origin); + self.placeholder_index_to_region.push(region); + region + } + } + } +} + /// The `Locations` type summarizes *where* region constraints are /// required to hold. Normally, this is at a particular point which /// created the obligation, but for constraints that the user gave, we @@ -886,15 +919,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(ref mut borrowck_context) = self.borrowck_context { constraint_conversion::ConstraintConversion::new( - self.infcx.tcx, + self.infcx, borrowck_context.universal_regions, self.region_bound_pairs, self.implicit_region_bound, self.param_env, locations, category, - &mut borrowck_context.constraints.outlives_constraints, - &mut borrowck_context.constraints.type_tests, + &mut borrowck_context.constraints, ).convert_all(&data); } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index 5c699b5295d..b82efb29f6e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -76,16 +76,20 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { } fn next_existential_region_var(&mut self) -> ty::Region<'tcx> { - let origin = NLLRegionVariableOrigin::Existential; - self.infcx.next_nll_region_var(origin) + if let Some(_) = &mut self.borrowck_context { + let origin = NLLRegionVariableOrigin::Existential; + self.infcx.next_nll_region_var(origin) + } else { + self.infcx.tcx.types.re_erased + } } fn next_placeholder_region(&mut self, placeholder: ty::Placeholder) -> ty::Region<'tcx> { - let origin = NLLRegionVariableOrigin::Placeholder(placeholder); if let Some(borrowck_context) = &mut self.borrowck_context { - borrowck_context.constraints.placeholder_indices.insert(placeholder); + borrowck_context.constraints.placeholder_region(self.infcx, placeholder) + } else { + self.infcx.tcx.types.re_erased } - self.infcx.next_nll_region_var(origin) } fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { From 3ef27d82e0623554962e96a9b06921326da861d2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 26 Sep 2018 12:35:24 -0400 Subject: [PATCH 03/11] allow canonicalized regions to carry universe and track max-universe But.. we don't really use it for anything right now. --- src/librustc/ich/impls_ty.rs | 4 +-- src/librustc/infer/canonical/canonicalizer.rs | 15 ++++++-- src/librustc/infer/canonical/mod.rs | 35 +++++++++++++++---- .../query/type_op/implied_outlives_bounds.rs | 20 ++++------- src/librustc/traits/query/type_op/outlives.rs | 16 +++------ src/test/mir-opt/basic_assignment.rs | 2 +- .../dump-adt-brace-struct.stderr | 2 +- .../user-annotations/dump-fn-method.stderr | 8 ++--- 8 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 7e5d19850f4..16a7f1425ab 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1268,7 +1268,7 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable for struct infer::canonical::Canonical<'tcx, V> { - variables, value + max_universe, variables, value } ); @@ -1284,7 +1284,7 @@ impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo { impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind { Ty(k), - Region + Region(ui), }); impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind { diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 2b085a3407c..536da55adc4 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -261,7 +261,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> | ty::ReScope(_) | ty::RePlaceholder(..) | ty::ReEmpty - | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r), + | ty::ReErased => self.canonicalize_region_mode + .canonicalize_free_region(self, r), ty::ReClosureBound(..) | ty::ReCanonical(_) => { bug!("canonical region encountered during canonicalization") @@ -353,6 +354,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { if !value.has_type_flags(needs_canonical_flags) { let out_value = gcx.lift(value).unwrap(); let canon_value = Canonical { + max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: out_value, }; @@ -383,7 +385,14 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables); + let max_universe = canonical_variables + .iter() + .map(|cvar| cvar.universe()) + .max() + .unwrap_or(ty::UniverseIndex::ROOT); + Canonical { + max_universe, variables: canonical_variables, value: out_value, } @@ -451,8 +460,10 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { } fn canonical_var_for_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + // TODO: root is not always what we want here, but we'll + // address that in a later commit. let info = CanonicalVarInfo { - kind: CanonicalVarKind::Region, + kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT), }; let b = self.canonical_var(info, r.into()); debug_assert_eq!(ty::INNERMOST, b.level); diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index e3bd407d17a..24864d77927 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -53,6 +53,7 @@ mod substitute; /// numbered starting from 0 in order of first appearance. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct Canonical<'gcx, V> { + pub max_universe: ty::UniverseIndex, pub variables: CanonicalVarInfos<'gcx>, pub value: V, } @@ -95,6 +96,12 @@ pub struct CanonicalVarInfo { pub kind: CanonicalVarKind, } +impl CanonicalVarInfo { + pub fn universe(self) -> ty::UniverseIndex { + self.kind.universe() + } +} + /// Describes the "kind" of the canonical variable. This is a "kind" /// in the type-theory sense of the term -- i.e., a "meta" type system /// that analyzes type-like values. @@ -104,7 +111,22 @@ pub enum CanonicalVarKind { Ty(CanonicalTyVarKind), /// Region variable `'?R`. - Region, + Region(ty::UniverseIndex), +} + + +impl CanonicalVarKind { + pub fn universe(self) -> ty::UniverseIndex { + match self { + // At present, we don't support higher-ranked + // quantification over types, so all type variables are in + // the root universe. + CanonicalVarKind::Ty(_) => ty::UniverseIndex::ROOT, + + // Region variables can be created in sub-universes. + CanonicalVarKind::Region(ui) => ui, + } + } } /// Rust actually has more than one category of type variables; @@ -220,8 +242,8 @@ impl<'gcx, V> Canonical<'gcx, V> { /// let b: Canonical<'tcx, (T, Ty<'tcx>)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical<'gcx, W> { - let Canonical { variables, value } = self; - Canonical { variables, value: map_op(value) } + let Canonical { max_universe, variables, value } = self; + Canonical { max_universe, variables, value: map_op(value) } } } @@ -293,8 +315,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ty.into() } - CanonicalVarKind::Region => self - .next_region_var(RegionVariableOrigin::MiscVariable(span)) + CanonicalVarKind::Region(ui) => self + .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(span), ui) .into(), } } @@ -314,6 +336,7 @@ CloneTypeFoldableImpls! { BraceStructTypeFoldableImpl! { impl<'tcx, C> TypeFoldable<'tcx> for Canonical<'tcx, C> { + max_universe, variables, value, } where C: TypeFoldable<'tcx> @@ -322,7 +345,7 @@ BraceStructTypeFoldableImpl! { BraceStructLiftImpl! { impl<'a, 'tcx, T> Lift<'tcx> for Canonical<'a, T> { type Lifted = Canonical<'tcx, T::Lifted>; - variables, value + max_universe, variables, value } where T: Lift<'tcx> } diff --git a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs index b113a322d37..d5233851db8 100644 --- a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs +++ b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs @@ -38,19 +38,13 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ImpliedOutlivesBounds< tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { - // FIXME the query should take a `ImpliedOutlivesBounds` - let Canonical { - variables, - value: - ParamEnvAnd { - param_env, - value: ImpliedOutlivesBounds { ty }, - }, - } = canonicalized; - let canonicalized = Canonical { - variables, - value: param_env.and(ty), - }; + // FIXME this `unchecked_map` is only necessary because the + // query is defined as taking a `ParamEnvAnd`; it should + // take a `ImpliedOutlivesBounds` instead + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let ImpliedOutlivesBounds { ty } = value; + param_env.and(ty) + }); tcx.implied_outlives_bounds(canonicalized) } diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index a36c5accd2a..cd7c6d76eab 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -59,18 +59,10 @@ where // FIXME convert to the type expected by the `dropck_outlives` // query. This should eventually be fixed by changing the // *underlying query*. - let Canonical { - variables, - value: - ParamEnvAnd { - param_env, - value: DropckOutlives { dropped_ty }, - }, - } = canonicalized; - let canonicalized = Canonical { - variables, - value: param_env.and(dropped_ty), - }; + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let DropckOutlives { dropped_ty } = value; + param_env.and(dropped_ty) + }); tcx.dropck_outlives(canonicalized) } diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index 7ca1d01f20b..75f19d133e0 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -56,7 +56,7 @@ fn main() { // StorageLive(_4); // _4 = std::option::Option>::None; // FakeRead(ForLet, _4); -// AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { variables: [], value: std::option::Option> }), projs: [] }); +// AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { max_universe: U0, variables: [], value: std::option::Option> }), projs: [] }); // StorageLive(_5); // StorageLive(_6); // _6 = move _4; diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr index 901ace59d33..88383190cbc 100644 --- a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr +++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr @@ -1,4 +1,4 @@ -error: user substs: Canonical { variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } --> $DIR/dump-adt-brace-struct.rs:28:5 | LL | SomeStruct:: { t: 22 }; //~ ERROR [u32] diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.stderr b/src/test/ui/nll/user-annotations/dump-fn-method.stderr index a26be359fc4..3beb994a4e8 100644 --- a/src/test/ui/nll/user-annotations/dump-fn-method.stderr +++ b/src/test/ui/nll/user-annotations/dump-fn-method.stderr @@ -1,22 +1,22 @@ -error: user substs: Canonical { variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:36:13 | LL | let x = foo::; //~ ERROR [u32] | ^^^^^^^^^^ -error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, u32, ?1], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, u32, ?1], user_self_ty: None } } --> $DIR/dump-fn-method.rs:42:13 | LL | let x = <_ as Bazoom>::method::<_>; //~ ERROR [?0, u32, ?1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user substs: Canonical { variables: [], value: UserSubsts { substs: [u8, u16, u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubsts { substs: [u8, u16, u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:46:13 | LL | let x = >::method::; //~ ERROR [u8, u16, u32] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, ?1, u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, ?1, u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:54:5 | LL | y.method::(44, 66); //~ ERROR [?0, ?1, u32] From 7f9ab60afba07cd9015d2b2138906894a5359351 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 27 Sep 2018 09:43:22 -0400 Subject: [PATCH 04/11] extend query response to potentially contain fresh universes The idea here is that an incoming query may refer to some universes, and they query response may contain fresh universes that go beyond those. When we instantiate the query response in the caller's scope, therefore, we map those new universes into fresh universes for the caller. --- src/librustc/ich/impls_ty.rs | 1 + src/librustc/infer/canonical/canonicalizer.rs | 35 ++++-- src/librustc/infer/canonical/mod.rs | 102 ++++++++++++++---- .../infer/canonical/query_response.rs | 32 +++++- src/librustc/ty/mod.rs | 2 + 5 files changed, 144 insertions(+), 28 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 16a7f1425ab..2109c4950e5 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1285,6 +1285,7 @@ impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo { impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind { Ty(k), Region(ui), + PlaceholderRegion(placeholder), }); impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind { diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 536da55adc4..72bf8f96fe1 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -162,11 +162,18 @@ struct CanonicalizeQueryResponse; impl CanonicalizeRegionMode for CanonicalizeQueryResponse { fn canonicalize_free_region( &self, - _canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, + canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match r { ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r, + ty::RePlaceholder(placeholder) => { + let info = CanonicalVarInfo { + kind: CanonicalVarKind::PlaceholderRegion(*placeholder), + }; + let cvar = canonicalizer.canonical_var(info, r.into()); + canonicalizer.tcx.mk_region(ty::ReCanonical(cvar.var)) + } _ => { // Other than `'static` or `'empty`, the query // response should be executing in a fully @@ -190,7 +197,7 @@ impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - canonicalizer.canonical_var_for_region(r) + canonicalizer.canonical_var_for_region_in_root_universe(r) } fn any(&self) -> bool { @@ -209,7 +216,7 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { if let ty::ReStatic = r { r } else { - canonicalizer.canonical_var_for_region(r) + canonicalizer.canonical_var_for_region_in_root_universe(r) } } @@ -252,7 +259,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> opportunistically resolved to {:?}", vid, r ); - self.canonical_var_for_region(r) + self.canonical_var_for_region_in_root_universe(r) } ty::ReStatic @@ -459,9 +466,23 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { } } - fn canonical_var_for_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - // TODO: root is not always what we want here, but we'll - // address that in a later commit. + /// Shorthand helper that creates a canonical region variable for + /// `r` (always in the root universe). The reason that we always + /// put these variables into the root universe is because this + /// method is used during **query construction:** in that case, we + /// are taking all the regions and just putting them into the most + /// generic context we can. This may generate solutions that don't + /// fit (e.g., that equate some region variable with a placeholder + /// it can't name) on the caller side, but that's ok, the caller + /// can figure that out. In the meantime, it maximizes our + /// caching. + /// + /// (This works because unification never fails -- and hence trait + /// selection is never affected -- due to a universe mismatch.) + fn canonical_var_for_region_in_root_universe( + &mut self, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { let info = CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT), }; diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 24864d77927..f2b7c6e0d0d 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -33,14 +33,14 @@ use infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::IndexVec; -use smallvec::SmallVec; use rustc_data_structures::sync::Lrc; use serialize::UseSpecializedDecodable; +use smallvec::SmallVec; use std::ops::Index; use syntax::source_map::Span; use ty::fold::TypeFoldable; use ty::subst::Kind; -use ty::{self, BoundTyIndex, Lift, Region, List, TyCtxt}; +use ty::{self, BoundTyIndex, Lift, List, Region, TyCtxt}; mod canonicalizer; @@ -80,13 +80,31 @@ pub struct CanonicalVarValues<'tcx> { /// various parts of it with canonical variables. This struct stores /// those replaced bits to remember for when we process the query /// result. -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct OriginalQueryValues<'tcx> { + /// Map from the universes that appear in the query to the + /// universes in the caller context. For the time being, we only + /// ever put ROOT values into the query, so this map is very + /// simple. + pub universe_map: SmallVec<[ty::UniverseIndex; 4]>, + /// This is equivalent to `CanonicalVarValues`, but using a /// `SmallVec` yields a significant performance win. pub var_values: SmallVec<[Kind<'tcx>; 8]>, } +impl Default for OriginalQueryValues<'tcx> { + fn default() -> Self { + let mut universe_map = SmallVec::default(); + universe_map.push(ty::UniverseIndex::ROOT); + + Self { + universe_map, + var_values: SmallVec::default(), + } + } +} + /// Information about a canonical variable that is included with the /// canonical value. This is sufficient information for code to create /// a copy of the canonical value in some other inference context, @@ -97,9 +115,17 @@ pub struct CanonicalVarInfo { } impl CanonicalVarInfo { - pub fn universe(self) -> ty::UniverseIndex { + pub fn universe(&self) -> ty::UniverseIndex { self.kind.universe() } + + pub fn is_existential(&self) -> bool { + match self.kind { + CanonicalVarKind::Ty(_) => true, + CanonicalVarKind::Region(_) => true, + CanonicalVarKind::PlaceholderRegion(..) => false, + } + } } /// Describes the "kind" of the canonical variable. This is a "kind" @@ -112,8 +138,12 @@ pub enum CanonicalVarKind { /// Region variable `'?R`. Region(ty::UniverseIndex), -} + /// A "placeholder" that represents "any region". Created when you + /// are solving a goal like `for<'a> T: Foo<'a>` to represent the + /// bound region `'a`. + PlaceholderRegion(ty::Placeholder), +} impl CanonicalVarKind { pub fn universe(self) -> ty::UniverseIndex { @@ -125,6 +155,7 @@ impl CanonicalVarKind { // Region variables can be created in sub-universes. CanonicalVarKind::Region(ui) => ui, + CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe, } } } @@ -242,8 +273,16 @@ impl<'gcx, V> Canonical<'gcx, V> { /// let b: Canonical<'tcx, (T, Ty<'tcx>)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical<'gcx, W> { - let Canonical { max_universe, variables, value } = self; - Canonical { max_universe, variables, value: map_op(value) } + let Canonical { + max_universe, + variables, + value, + } = self; + Canonical { + max_universe, + variables, + value: map_op(value), + } } } @@ -271,35 +310,50 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + // For each universe that is referred to in the incoming + // query, create a universe in our local inference context. In + // practice, as of this writing, all queries have no universes + // in them, so this code has no effect, but it is looking + // forward to the day when we *do* want to carry universes + // through into queries. + let universes: IndexVec = std::iter::once(ty::UniverseIndex::ROOT) + .chain((0..canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) + .collect(); + let canonical_inference_vars = - self.fresh_inference_vars_for_canonical_vars(span, canonical.variables); + self.instantiate_canonical_vars(span, canonical.variables, |ui| universes[ui]); let result = canonical.substitute(self.tcx, &canonical_inference_vars); (result, canonical_inference_vars) } /// Given the "infos" about the canonical variables from some - /// canonical, creates fresh inference variables with the same - /// characteristics. You can then use `substitute` to instantiate - /// the canonical variable with these inference variables. - fn fresh_inference_vars_for_canonical_vars( + /// canonical, creates fresh variables with the same + /// characteristics (see `instantiate_canonical_var` for + /// details). You can then use `substitute` to instantiate the + /// canonical variable with these inference variables. + fn instantiate_canonical_vars( &self, span: Span, variables: &List, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { let var_values: IndexVec> = variables .iter() - .map(|info| self.fresh_inference_var_for_canonical_var(span, *info)) + .map(|info| self.instantiate_canonical_var(span, *info, &universe_map)) .collect(); CanonicalVarValues { var_values } } /// Given the "info" about a canonical variable, creates a fresh - /// inference variable with the same characteristics. - fn fresh_inference_var_for_canonical_var( + /// variable for it. If this is an existentially quantified + /// variable, then you'll get a new inference variable; if it is a + /// universally quantified variable, you get a placeholder. + fn instantiate_canonical_var( &self, span: Span, cv_info: CanonicalVarInfo, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> Kind<'tcx> { match cv_info.kind { CanonicalVarKind::Ty(ty_kind) => { @@ -315,9 +369,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ty.into() } - CanonicalVarKind::Region(ui) => self - .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(span), ui) - .into(), + CanonicalVarKind::Region(ui) => self.next_region_var_in_universe( + RegionVariableOrigin::MiscVariable(span), + universe_map(ui), + ).into(), + + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { universe, name }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::Placeholder { + universe: universe_mapped, + name, + }; + self.tcx + .mk_region(ty::RePlaceholder(placeholder_mapped)) + .into() + } } } } diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 38788186eb0..b3ce5eb7e56 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -394,6 +394,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { original_values, query_response, ); + // For each new universe created in the query result that did + // not appear in the original query, create a local + // superuniverse. + let mut universe_map = original_values.universe_map.clone(); + let num_universes_in_query = original_values.universe_map.len(); + let num_universes_in_response = query_response.max_universe.as_usize() + 1; + for _ in num_universes_in_query..num_universes_in_response { + universe_map.push(self.create_next_universe()); + } + assert!(universe_map.len() >= 1); // always have the root universe + assert_eq!( + universe_map[ty::UniverseIndex::ROOT.as_usize()], + ty::UniverseIndex::ROOT + ); + // Every canonical query result includes values for each of // the inputs to the query. Therefore, we begin by unifying // these values with the original inputs that were @@ -440,9 +455,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .variables .iter() .enumerate() - .map(|(index, info)| opt_values[BoundTyIndex::new(index)].unwrap_or_else(|| - self.fresh_inference_var_for_canonical_var(cause.span, *info) - )) + .map(|(index, info)| { + if info.is_existential() { + match opt_values[BoundTyIndex::new(index)] { + Some(k) => k, + None => self.instantiate_canonical_var(cause.span, *info, |u| { + universe_map[u.as_usize()] + }), + } + } else { + self.instantiate_canonical_var(cause.span, *info, |u| { + universe_map[u.as_usize()] + }) + } + }) .collect(), }; diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 48ba69fee1c..4e214394399 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1540,6 +1540,8 @@ pub struct Placeholder { pub name: BoundRegion, } +impl_stable_hash_for!(struct Placeholder { universe, name }); + /// When type checking, we use the `ParamEnv` to track /// details about the set of where-clauses that are in scope at this /// particular point. From 80d9f8bb48d2799307f84dd8fa69884c5a407ac6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Oct 2018 14:47:51 -0400 Subject: [PATCH 05/11] when canonicalizing query responses, preserve infer-var universes --- src/librustc/infer/canonical/canonicalizer.rs | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 72bf8f96fe1..94bb17988c7 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -167,12 +167,20 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { ) -> ty::Region<'tcx> { match r { ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r, - ty::RePlaceholder(placeholder) => { - let info = CanonicalVarInfo { + ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder), - }; - let cvar = canonicalizer.canonical_var(info, r.into()); - canonicalizer.tcx.mk_region(ty::ReCanonical(cvar.var)) + }, + r, + ), + ty::ReVar(vid) => { + let universe = canonicalizer.region_var_universe(*vid); + canonicalizer.canonical_var_for_region( + CanonicalVarInfo { + kind: CanonicalVarKind::Region(universe), + }, + r, + ) } _ => { // Other than `'static` or `'empty`, the query @@ -259,7 +267,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> opportunistically resolved to {:?}", vid, r ); - self.canonical_var_for_region_in_root_universe(r) + self.canonicalize_region_mode + .canonicalize_free_region(self, r) } ty::ReStatic @@ -483,9 +492,29 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { &mut self, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - let info = CanonicalVarInfo { - kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - }; + self.canonical_var_for_region( + CanonicalVarInfo { + kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + }, + r, + ) + } + + /// Returns the universe in which `vid` is defined. + fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex { + self.infcx + .unwrap() + .borrow_region_constraints() + .var_universe(vid) + } + + /// Create a canonical variable (with the given `info`) + /// representing the region `r`; return a region referencing it. + fn canonical_var_for_region( + &mut self, + info: CanonicalVarInfo, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { let b = self.canonical_var(info, r.into()); debug_assert_eq!(ty::INNERMOST, b.level); self.tcx().mk_region(ty::ReCanonical(b.var)) From bf51840952b701606fa6aa5e076b4e27fb8d9ee8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Oct 2018 16:36:31 -0400 Subject: [PATCH 06/11] distinguish user-type annotations from other annotations In particular, we don't want to preserve the universes for the `'_` variables that appear in there. And we don't expect to find any placeholders, which justifies this as harmless. (In particular, if you have a query like `Foo(!1, !2, ?3)`, then you care about the universe of `?3`, since it may control whether `?3 = !1` and `?3 = !2` is a valid answer. But without any placeholders, we don't really care: any placeholders that would appear in the output must therefore come from some fresh universe anyway.) --- src/librustc/infer/canonical/canonicalizer.rs | 37 +++++++++++++++++++ src/librustc_typeck/check/mod.rs | 6 +-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 94bb17988c7..cc6e4df0710 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -107,6 +107,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) } + pub fn canonicalize_user_type_annotation(&self, value: &V) -> Canonicalized<'gcx, V> + where + V: TypeFoldable<'tcx> + Lift<'gcx>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + &CanonicalizeUserTypeAnnotation, + &mut query_state, + ) + } + /// A hacky variant of `canonicalize_query` that does not /// canonicalize `'static`. Unfortunately, the existing leak /// check treaks `'static` differently in some cases (see also @@ -197,6 +211,29 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { } } +struct CanonicalizeUserTypeAnnotation; + +impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { + fn canonicalize_free_region( + &self, + canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + match r { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r, + ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), + _ => { + // We only expect region names that the user can type. + bug!("unexpected region in query response: `{:?}`", r) + } + } + } + + fn any(&self) -> bool { + false + } +} + struct CanonicalizeAllFreeRegions; impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7e25694d559..8bd4887fa7c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -976,7 +976,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> { o_ty }; - let c_ty = self.fcx.inh.infcx.canonicalize_response(&revealed_ty); + let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(&revealed_ty); debug!("visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}", ty.hir_id, o_ty, revealed_ty, c_ty); self.fcx.tables.borrow_mut().user_provided_tys_mut().insert(ty.hir_id, c_ty); @@ -2137,7 +2137,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { method.substs[i] } }); - self.infcx.canonicalize_response(&UserSubsts { + self.infcx.canonicalize_user_type_annotation(&UserSubsts { substs: just_method_substs, user_self_ty: None, // not relevant here }) @@ -2181,7 +2181,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ); if !substs.is_noop() { - let user_substs = self.infcx.canonicalize_response(&UserSubsts { + let user_substs = self.infcx.canonicalize_user_type_annotation(&UserSubsts { substs, user_self_ty, }); From d4e4e374e76b3769207347a81e64b26b6a2b657f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 3 Oct 2018 10:07:16 -0400 Subject: [PATCH 07/11] apply minimum bounds when checking closure signature Required for test expect-fn-supply-fn.rs to pass; otherwise we have unconstrained inference variables that get inferred to `'empty`. --- src/librustc/traits/select.rs | 6 ++++++ src/librustc_typeck/check/closure.rs | 30 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 2ea16823cc6..cdc7e3656d4 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -3666,8 +3666,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { closure_def_id: DefId, substs: ty::ClosureSubsts<'tcx>, ) -> ty::PolyTraitRef<'tcx> { + debug!( + "closure_trait_ref_unnormalized(obligation={:?}, closure_def_id={:?}, substs={:?})", + obligation, closure_def_id, substs, + ); let closure_type = self.infcx.closure_sig(closure_def_id, substs); + debug!("closure_trait_ref_unnormalized: closure_type = {:?}", closure_type); + // (1) Feels icky to skip the binder here, but OTOH we know // that the self-type is an unboxed closure type and hence is // in fact unparameterized (or at least does not reference any diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 3f4d187813d..fb7c237a536 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -13,10 +13,12 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use astconv::AstConv; +use middle::region; use rustc::hir::def_id::DefId; use rustc::infer::{InferOk, InferResult}; use rustc::infer::LateBoundRegionConversionTime; use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::traits::Obligation; use rustc::traits::error_reporting::ArgKind; use rustc::ty::{self, ToPolyTraitRef, Ty, GenericParamDefKind}; use rustc::ty::fold::TypeFoldable; @@ -479,7 +481,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Along the way, it also writes out entries for types that the user // wrote into our tables, which are then later used by the privacy // check. - match self.check_supplied_sig_against_expectation(expr_def_id, decl, &closure_sigs) { + match self.check_supplied_sig_against_expectation(expr_def_id, decl, body, &closure_sigs) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), Err(_) => return self.sig_of_closure_no_expectation(expr_def_id, decl, body), } @@ -523,6 +525,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { &self, expr_def_id: DefId, decl: &hir::FnDecl, + body: &hir::Body, expected_sigs: &ClosureSignatures<'tcx>, ) -> InferResult<'tcx, ()> { // Get the signature S that the user gave. @@ -575,6 +578,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } = self.at(cause, self.param_env) .eq(*expected_ty, supplied_ty)?; all_obligations.extend(obligations); + + // Also, require that the supplied type must outlive + // the closure body. + let closure_body_region = self.tcx.mk_region( + ty::ReScope( + region::Scope { + id: body.value.hir_id.local_id, + data: region::ScopeData::Node, + }, + ), + ); + all_obligations.push( + Obligation::new( + cause.clone(), + self.param_env, + ty::Predicate::TypeOutlives( + ty::Binder::dummy( + ty::OutlivesPredicate( + supplied_ty, + closure_body_region, + ), + ), + ), + ), + ); } let (supplied_output_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var( From b24b88734d7aab59184d30209bc6053cc2ecf3a4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 24 Oct 2018 08:51:16 -0400 Subject: [PATCH 08/11] select.rs: rustfmt --- src/librustc/traits/select.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index cdc7e3656d4..e2a5fdd3622 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1368,7 +1368,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // Winnow, but record the exact outcome of evaluation, which // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates.into_iter() + let mut candidates = candidates + .into_iter() .map(|c| match self.evaluate_candidate(stack, &c) { Ok(eval) if eval.may_apply() => Ok(Some(EvaluatedCandidate { candidate: c, @@ -1377,8 +1378,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Ok(_) => Ok(None), Err(OverflowError) => Err(Overflow), }) - .flat_map(Result::transpose) - .collect::, _>>()?; + .flat_map(Result::transpose) + .collect::, _>>()?; debug!( "winnowed to {} candidates for {:?}: {:?}", @@ -3004,9 +3005,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let self_ty = self.infcx .shallow_resolve(*obligation.self_ty().skip_binder()); let poly_trait_ref = match self_ty.sty { - ty::Dynamic(ref data, ..) => { - data.principal().with_self_ty(self.tcx(), self_ty) - } + ty::Dynamic(ref data, ..) => data.principal().with_self_ty(self.tcx(), self_ty), _ => span_bug!(obligation.cause.span, "object candidate with non-object"), }; @@ -3672,7 +3671,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ); let closure_type = self.infcx.closure_sig(closure_def_id, substs); - debug!("closure_trait_ref_unnormalized: closure_type = {:?}", closure_type); + debug!( + "closure_trait_ref_unnormalized: closure_type = {:?}", + closure_type + ); // (1) Feels icky to skip the binder here, but OTOH we know // that the self-type is an unboxed closure type and hence is From a1be20c9ce4e5cf934f055e0dc6776ff8382f8e9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 24 Oct 2018 08:51:39 -0400 Subject: [PATCH 09/11] error_reporting/mod.rs: rustfmt --- .../nll/region_infer/error_reporting/mod.rs | 269 ++++++++++-------- 1 file changed, 152 insertions(+), 117 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index 64135195637..bf150dbe460 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::ConstraintDescription; -use borrow_check::nll::constraints::{OutlivesConstraint}; +use borrow_check::nll::constraints::OutlivesConstraint; use borrow_check::nll::region_infer::RegionInferenceContext; use borrow_check::nll::type_check::Locations; use borrow_check::nll::universal_regions::DefiningTy; -use util::borrowck_errors::{BorrowckErrors, Origin}; +use borrow_check::nll::ConstraintDescription; use rustc::hir::def_id::DefId; use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::InferCtxt; @@ -22,9 +21,10 @@ use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::{Diagnostic, DiagnosticBuilder}; use std::collections::VecDeque; +use syntax::errors::Applicability; use syntax::symbol::keywords; use syntax_pos::Span; -use syntax::errors::Applicability; +use util::borrowck_errors::{BorrowckErrors, Origin}; mod region_name; mod var_name; @@ -76,9 +76,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("best_blame_constraint(from_region={:?})", from_region); // Find all paths - let (path, target_region) = self - .find_constraint_paths_between_regions(from_region, target_test) - .unwrap(); + let (path, target_region) = + self.find_constraint_paths_between_regions(from_region, target_test) + .unwrap(); debug!( "best_blame_constraint: path={:#?}", path.iter() @@ -92,8 +92,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); // Classify each of the constraints along the path. - let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path - .iter() + let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path.iter() .map(|constraint| { if constraint.category == ConstraintCategory::ClosureBounds { self.retrieve_closure_constraint_info(mir, &constraint) @@ -137,13 +136,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { | ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false, - ConstraintCategory::TypeAnnotation - | ConstraintCategory::Return => true, + ConstraintCategory::TypeAnnotation | ConstraintCategory::Return => true, _ => constraint_sup_scc != target_scc, } }); if let Some(i) = best_choice { - return categorized_path[i] + return categorized_path[i]; } // If that search fails, that is.. unusual. Maybe everything @@ -206,9 +204,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // enqueue any regions we find, keeping track of how we // reached them. let fr_static = self.universal_regions.fr_static; - for constraint in self.constraint_graph.outgoing_edges(r, - &self.constraints, - fr_static) { + for constraint in self.constraint_graph + .outgoing_edges(r, &self.constraints, fr_static) + { assert_eq!(constraint.sup, r); let sub_region = constraint.sub; if let Trace::NotVisited = context[sub_region] { @@ -240,11 +238,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (category, _, span) = self.best_blame_constraint( - mir, - fr, - |r| r == outlived_fr - ); + let (category, _, span) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { @@ -260,20 +254,45 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.universal_regions.is_local_free_region(outlived_fr), ); - debug!("report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}", - fr_is_local, outlived_fr_is_local, category); + debug!( + "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}", + fr_is_local, outlived_fr_is_local, category + ); match (category, fr_is_local, outlived_fr_is_local) { - (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => - self.report_fnmut_error(mir, infcx, mir_def_id, fr, outlived_fr, span, - errors_buffer), - (ConstraintCategory::Assignment, true, false) | - (ConstraintCategory::CallArgument, true, false) => - self.report_escaping_data_error(mir, infcx, mir_def_id, fr, outlived_fr, - category, span, errors_buffer), - _ => - self.report_general_error(mir, infcx, mir_def_id, fr, fr_is_local, - outlived_fr, outlived_fr_is_local, - category, span, errors_buffer), + (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => { + self.report_fnmut_error( + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + span, + errors_buffer, + ) + } + (ConstraintCategory::Assignment, true, false) + | (ConstraintCategory::CallArgument, true, false) => self.report_escaping_data_error( + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + category, + span, + errors_buffer, + ), + _ => self.report_general_error( + mir, + infcx, + mir_def_id, + fr, + fr_is_local, + outlived_fr, + outlived_fr_is_local, + category, + span, + errors_buffer, + ), }; } @@ -303,10 +322,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { span: Span, errors_buffer: &mut Vec, ) { - let mut diag = infcx.tcx.sess.struct_span_err( - span, - "captured variable cannot escape `FnMut` closure body", - ); + let mut diag = infcx + .tcx + .sess + .struct_span_err(span, "captured variable cannot escape `FnMut` closure body"); // We should check if the return type of this closure is in fact a closure - in that // case, we can special case the error further. @@ -318,27 +337,28 @@ impl<'tcx> RegionInferenceContext<'tcx> { "returns a reference to a captured variable which escapes the closure body" }; - diag.span_label( - span, - message, - ); + diag.span_label(span, message); - match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1).source { - RegionNameSource::NamedEarlyBoundRegion(fr_span) | - RegionNameSource::NamedFreeRegion(fr_span) | - RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) | - RegionNameSource::CannotMatchHirTy(fr_span, _) | - RegionNameSource::MatchedHirTy(fr_span) | - RegionNameSource::MatchedAdtAndSegment(fr_span) | - RegionNameSource::AnonRegionFromUpvar(fr_span, _) | - RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => { + match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1) + .source + { + RegionNameSource::NamedEarlyBoundRegion(fr_span) + | RegionNameSource::NamedFreeRegion(fr_span) + | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) + | RegionNameSource::CannotMatchHirTy(fr_span, _) + | RegionNameSource::MatchedHirTy(fr_span) + | RegionNameSource::MatchedAdtAndSegment(fr_span) + | RegionNameSource::AnonRegionFromUpvar(fr_span, _) + | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => { diag.span_label(fr_span, "inferred to be a `FnMut` closure"); - }, - _ => {}, + } + _ => {} } - diag.note("`FnMut` closures only have access to their captured variables while they are \ - executing..."); + diag.note( + "`FnMut` closures only have access to their captured variables while they are \ + executing...", + ); diag.note("...therefore, they cannot allow references to captured variables to escape"); diag.buffer(errors_buffer); @@ -375,7 +395,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { DefiningTy::Closure(..) => "closure", DefiningTy::Generator(..) => "generator", DefiningTy::FnDef(..) => "function", - DefiningTy::Const(..) => "const" + DefiningTy::Const(..) => "const", }; // Revert to the normal error in these cases. @@ -384,12 +404,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { || (category == ConstraintCategory::Assignment && escapes_from == "function") || escapes_from == "const" { - return self.report_general_error(mir, infcx, mir_def_id, - fr, true, outlived_fr, false, - category, span, errors_buffer); + return self.report_general_error( + mir, + infcx, + mir_def_id, + fr, + true, + outlived_fr, + false, + category, + span, + errors_buffer, + ); } - let mut diag = infcx.tcx.borrowed_data_escapes_closure(span, escapes_from, Origin::Mir); + let mut diag = infcx + .tcx + .borrowed_data_escapes_closure(span, escapes_from, Origin::Mir); if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span { diag.span_label( @@ -410,7 +441,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { ), ); - diag.span_label(span, format!("`{}` escapes the {} body here", fr_name, escapes_from)); + diag.span_label( + span, + format!("`{}` escapes the {} body here", fr_name, escapes_from), + ); } diag.buffer(errors_buffer); @@ -452,31 +486,41 @@ impl<'tcx> RegionInferenceContext<'tcx> { let counter = &mut 1; let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter); fr_name.highlight_region_name(&mut diag); - let outlived_fr_name = self.give_region_a_name( - infcx, mir, mir_def_id, outlived_fr, counter); + let outlived_fr_name = + self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, counter); outlived_fr_name.highlight_region_name(&mut diag); - let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { "closure" } else { "function" }; + let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { + "closure" + } else { + "function" + }; match (category, outlived_fr_is_local, fr_is_local) { (ConstraintCategory::Return, true, _) => { - diag.span_label(span, format!( - "{} was supposed to return data with lifetime `{}` but it is returning \ - data with lifetime `{}`", - mir_def_name, outlived_fr_name, fr_name - )); - }, + diag.span_label( + span, + format!( + "{} was supposed to return data with lifetime `{}` but it is returning \ + data with lifetime `{}`", + mir_def_name, outlived_fr_name, fr_name + ), + ); + } _ => { - diag.span_label(span, format!( - "{}requires that `{}` must outlive `{}`", - category.description(), fr_name, outlived_fr_name, - )); - }, + diag.span_label( + span, + format!( + "{}requires that `{}` must outlive `{}`", + category.description(), + fr_name, + outlived_fr_name, + ), + ); + } } - self.add_static_impl_trait_suggestion( - infcx, &mut diag, fr, fr_name, outlived_fr, - ); + self.add_static_impl_trait_suggestion(infcx, &mut diag, fr, fr_name, outlived_fr); diag.buffer(errors_buffer); } @@ -499,17 +543,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr_name: RegionName, outlived_fr: RegionVid, ) { - if let ( - Some(f), - Some(ty::RegionKind::ReStatic) - ) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { + if let (Some(f), Some(ty::RegionKind::ReStatic)) = + (self.to_error_region(fr), self.to_error_region(outlived_fr)) + { if let Some(ty::TyS { sty: ty::TyKind::Opaque(did, substs), .. - }) = infcx.tcx.is_suitable_region(f) - .map(|r| r.def_id) - .map(|id| infcx.tcx.return_type_impl_trait(id)) - .unwrap_or(None) + }) = infcx + .tcx + .is_suitable_region(f) + .map(|r| r.def_id) + .map(|id| infcx.tcx.return_type_impl_trait(id)) + .unwrap_or(None) { // Check whether or not the impl trait return type is intended to capture // data with the static lifetime. @@ -522,10 +567,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut found = false; for predicate in bounds.predicates { if let ty::Predicate::TypeOutlives(binder) = predicate { - if let ty::OutlivesPredicate( - _, - ty::RegionKind::ReStatic - ) = binder.skip_binder() { + if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) = + binder.skip_binder() + { found = true; break; } @@ -535,18 +579,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { found }; - debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}", - has_static_predicate); + debug!( + "add_static_impl_trait_suggestion: has_static_predicate={:?}", + has_static_predicate + ); let static_str = keywords::StaticLifetime.name(); // If there is a static predicate, then the only sensible suggestion is to replace // fr with `'static`. if has_static_predicate { - diag.help( - &format!( - "consider replacing `{}` with `{}`", - fr_name, static_str, - ), - ); + diag.help(&format!( + "consider replacing `{}` with `{}`", + fr_name, static_str, + )); } else { // Otherwise, we should suggest adding a constraint on the return type. let span = infcx.tcx.def_span(*did); @@ -581,13 +625,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { borrow_region: RegionVid, outlived_region: RegionVid, ) -> (ConstraintCategory, bool, Span, RegionName) { - let (category, from_closure, span) = self.best_blame_constraint( - mir, - borrow_region, - |r| r == outlived_region - ); - let outlived_fr_name = self.give_region_a_name( - infcx, mir, mir_def_id, outlived_region, &mut 1); + let (category, from_closure, span) = + self.best_blame_constraint(mir, borrow_region, |r| r == outlived_region); + let outlived_fr_name = + self.give_region_a_name(infcx, mir, mir_def_id, outlived_region, &mut 1); (category, from_closure, span, outlived_fr_name) } @@ -596,10 +637,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); // Find all paths - let (_path, r) = - self.find_constraint_paths_between_regions(fr1, |r| { - self.liveness_constraints.contains(r, elem) - }).unwrap(); + let (_path, r) = self.find_constraint_paths_between_regions(fr1, |r| { + self.liveness_constraints.contains(r, elem) + }).unwrap(); r } @@ -617,27 +657,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn retrieve_closure_constraint_info( &self, mir: &Mir<'tcx>, - constraint: &OutlivesConstraint + constraint: &OutlivesConstraint, ) -> (ConstraintCategory, bool, Span) { let loc = match constraint.locations { Locations::All(span) => return (constraint.category, false, span), Locations::Single(loc) => loc, }; - let opt_span_category = self - .closure_bounds_mapping[&loc] - .get(&(constraint.sup, constraint.sub)); + let opt_span_category = + self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub)); opt_span_category .map(|&(category, span)| (category, true, span)) .unwrap_or((constraint.category, false, mir.source_info(loc).span)) } /// Returns `true` if a closure is inferred to be an `FnMut` closure. - crate fn is_closure_fn_mut( - &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - fr: RegionVid, - ) -> bool { + crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, '_, 'tcx>, fr: RegionVid) -> bool { if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) { if let ty::BoundRegion::BrEnv = free_region.bound_region { if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty { From 740117f905b5e0c49659574087f6f7c80edf631a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Oct 2018 12:10:49 -0400 Subject: [PATCH 10/11] fix bug in NLL error reporting Account for incompatible universes and higher-ranked subtyping. --- src/librustc/ty/mod.rs | 9 +- .../nll/region_infer/error_reporting/mod.rs | 96 +++++++++++++++++-- .../borrow_check/nll/region_infer/mod.rs | 12 +++ .../borrow_check/nll/type_check/mod.rs | 2 +- 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4e214394399..9f1af50a6d5 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1522,10 +1522,17 @@ impl UniverseIndex { /// True if `self` can name a name from `other` -- in other words, /// if the set of names in `self` is a superset of those in - /// `other`. + /// `other` (`self >= other`). pub fn can_name(self, other: UniverseIndex) -> bool { self.private >= other.private } + + /// True if `self` cannot name some names from `other` -- in other + /// words, if the set of names in `self` is a strict subset of + /// those in `other` (`self < other`). + pub fn cannot_name(self, other: UniverseIndex) -> bool { + self.private < other.private + } } /// The "placeholder index" fully defines a placeholder region. diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index bf150dbe460..3358e5851f9 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -16,6 +16,7 @@ use borrow_check::nll::ConstraintDescription; use rustc::hir::def_id::DefId; use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::InferCtxt; +use rustc::infer::NLLRegionVariableOrigin; use rustc::mir::{ConstraintCategory, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; @@ -177,6 +178,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { deque.push_back(from_region); while let Some(r) = deque.pop_front() { + debug!( + "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}", + from_region, + r, + self.region_value_str(r), + ); + // Check if we reached the region we were looking for. If so, // we can reconstruct the path that led to it and return it. if target_test(r) { @@ -238,7 +246,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (category, _, span) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr); + let (category, _, span) = self.best_blame_constraint(mir, fr, |r| { + self.provides_universal_region(r, fr, outlived_fr) + }); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { @@ -296,6 +306,33 @@ impl<'tcx> RegionInferenceContext<'tcx> { }; } + /// We have a constraint `fr1: fr2` that is not satisfied, where + /// `fr2` represents some universal region. Here, `r` is some + /// region where we know that `fr1: r` and this function has the + /// job of determining whether `r` is "to blame" for the fact that + /// `fr1: fr2` is required. + /// + /// This is true under two conditions: + /// + /// - `r == fr2` + /// - `fr2` is `'static` and `r` is some placeholder in a universe + /// that cannot be named by `fr1`; in that case, we will require + /// that `fr1: 'static` because it is the only way to `fr1: r` to + /// be satisfied. (See `add_incompatible_universe`.) + fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool { + debug!( + "provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", + r, fr1, fr2 + ); + let result = { + r == fr2 || { + fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r) + } + }; + debug!("provides_universal_region: result = {:?}", result); + result + } + /// Report a specialized error when `FnMut` closures return a reference to a captured variable. /// This function expects `fr` to be local and `outlived_fr` to not be local. /// @@ -636,11 +673,37 @@ impl<'tcx> RegionInferenceContext<'tcx> { // `elem`. crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); - // Find all paths - let (_path, r) = self.find_constraint_paths_between_regions(fr1, |r| { + self.find_constraint_paths_between_regions(fr1, |r| { + // First look for some `r` such that `fr1: r` and `r` is live at `elem` + debug!( + "find_sub_region_live_at: liveness_constraints for {:?} are {:?}", + r, + self.liveness_constraints.region_value_str(r), + ); self.liveness_constraints.contains(r, elem) - }).unwrap(); - r + }).or_else(|| { + // If we fail to find that, we may find some `r` such that + // `fr1: r` and `r` is a placeholder from some universe + // `fr1` cannot name. This would force `fr1` to be + // `'static`. + self.find_constraint_paths_between_regions(fr1, |r| { + self.cannot_name_placeholder(fr1, r) + }) + }) + .or_else(|| { + // If we fail to find THAT, it may be that `fr1` is a + // placeholder that cannot "fit" into its SCC. In that + // case, there should be some `r` where `fr1: r`, both + // `fr1` and `r` are in the same SCC, and `fr1` is a + // placeholder that `r` cannot name. We can blame that + // edge. + self.find_constraint_paths_between_regions(fr1, |r| { + self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r) + && self.cannot_name_placeholder(r, fr1) + }) + }) + .map(|(_path, r)| r) + .unwrap() } // Finds a good span to blame for the fact that `fr1` outlives `fr2`. @@ -650,7 +713,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr1: RegionVid, fr2: RegionVid, ) -> (ConstraintCategory, Span) { - let (category, _, span) = self.best_blame_constraint(mir, fr1, |r| r == fr2); + let (category, _, span) = + self.best_blame_constraint(mir, fr1, |r| self.provides_universal_region(r, fr1, fr2)); (category, span) } @@ -684,4 +748,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { false } + + /// If `r2` represents a placeholder region, then this returns + /// true if `r1` cannot name that placeholder in its + /// value. Otherwise, returns false. + fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { + debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); + + match self.definitions[r2].origin { + NLLRegionVariableOrigin::Placeholder(placeholder) => { + let universe1 = self.definitions[r1].universe; + debug!( + "cannot_name_value_of: universe1={:?} placeholder={:?}", + universe1, placeholder + ); + universe1.cannot_name(placeholder.universe) + } + + NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential => false, + } + } } 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 0e44c8b36a9..376f4459242 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -345,6 +345,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { if scc_universe.can_name(placeholder.universe) { self.scc_values.add_element(scc, placeholder); } else { + debug!( + "init_free_and_bound_regions: placeholder {:?} is \ + not compatible with universe {:?} of its SCC {:?}", + placeholder, + scc_universe, + scc, + ); self.add_incompatible_universe(scc); } } @@ -471,6 +478,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut constraints: Vec<_> = self.constraints.iter().collect(); constraints.sort(); constraints + .into_iter() + .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) + .collect::>() }); // To propagate constraints, we walk the DAG induced by the @@ -560,6 +570,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `'a` with `'b` and not `'static`. But it will have to do for /// now. fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) { + debug!("add_incompatible_universe(scc={:?})", scc); + let fr_static = self.universal_regions.fr_static; self.scc_values.add_all_points(scc); self.scc_values.add_element(scc, fr_static); 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 eae41c81ff2..8ef810bba9c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -782,7 +782,7 @@ impl MirTypeckRegionConstraints<'tcx> { Some(&v) => v, None => { let origin = NLLRegionVariableOrigin::Placeholder(placeholder); - let region = infcx.next_nll_region_var(origin); + let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); self.placeholder_index_to_region.push(region); region } From c244fd79f22f7a78016f20fa252d93bae474f4bd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 15 Oct 2018 11:55:20 -0400 Subject: [PATCH 11/11] kill old-style-lub warnings --- src/librustc/infer/glb.rs | 24 +- src/librustc/infer/higher_ranked/mod.rs | 256 ------------------ src/librustc/infer/lub.rs | 24 +- src/librustc/ty/error.rs | 11 - src/librustc/ty/structural_impls.rs | 2 - src/test/ui/lub-glb/old-lub-glb-hr.stderr | 2 - src/test/ui/lub-glb/old-lub-glb-object.stderr | 2 - 7 files changed, 4 insertions(+), 317 deletions(-) diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index fd14e0e40e2..8968c5949b6 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -15,7 +15,6 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; -use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Greatest lower bound" (common subtype) @@ -76,31 +75,12 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> where T: Relate<'tcx> { debug!("binders(a={:?}, b={:?})", a, b); - let was_error = self.infcx().probe(|_snapshot| { - // Subtle: use a fresh combine-fields here because we recover - // from Err. Doing otherwise could propagate obligations out - // through our `self.obligations` field. - self.infcx() - .combine_fields(self.fields.trace.clone(), self.fields.param_env) - .higher_ranked_glb(a, b, self.a_is_expected) - .is_err() - }); - debug!("binders: was_error={:?}", was_error); // When higher-ranked types are involved, computing the LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - match self.relate_with_variance(ty::Variance::Invariant, a, b) { - Ok(_) => Ok(a.clone()), - Err(err) => { - debug!("binders: error occurred, was_error={:?}", was_error); - if !was_error { - Err(TypeError::OldStyleLUB(Box::new(err))) - } else { - Err(err) - } - } - } + self.relate_with_variance(ty::Variance::Invariant, a, b)?; + Ok(a.clone()) } } diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index d85a3e84f85..3e08a4e021a 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -22,7 +22,6 @@ use super::region_constraints::{TaintDirections}; use ty::{self, TyCtxt, Binder, TypeFoldable}; use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; -use std::collections::BTreeMap; use syntax_pos::Span; use util::nodemap::{FxHashMap, FxHashSet}; @@ -202,261 +201,6 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { Ok(HrMatchResult { value: a_value }) }); } - - pub fn higher_ranked_lub(&mut self, a: &Binder, b: &Binder, a_is_expected: bool) - -> RelateResult<'tcx, Binder> - where T: Relate<'tcx> - { - // Start a snapshot so we can examine "all bindings that were - // created as part of this type comparison". - return self.infcx.commit_if_ok(|snapshot| { - // Instantiate each bound region with a fresh region variable. - let span = self.trace.cause.span; - let (a_with_fresh, a_map) = - self.infcx.replace_late_bound_regions_with_fresh_var( - span, HigherRankedType, a); - let (b_with_fresh, _) = - self.infcx.replace_late_bound_regions_with_fresh_var( - span, HigherRankedType, b); - - // Collect constraints. - let result0 = - self.lub(a_is_expected).relate(&a_with_fresh, &b_with_fresh)?; - let result0 = - self.infcx.resolve_type_vars_if_possible(&result0); - debug!("lub result0 = {:?}", result0); - - // Generalize the regions appearing in result0 if possible - let new_vars = self.infcx.region_vars_confined_to_snapshot(snapshot); - let span = self.trace.cause.span; - let result1 = - fold_regions_in( - self.tcx(), - &result0, - |r, debruijn| generalize_region(self.infcx, span, snapshot, debruijn, - &new_vars, &a_map, r)); - - debug!("lub({:?},{:?}) = {:?}", - a, - b, - result1); - - Ok(ty::Binder::bind(result1)) - }); - - fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - span: Span, - snapshot: &CombinedSnapshot<'a, 'tcx>, - debruijn: ty::DebruijnIndex, - new_vars: &[ty::RegionVid], - a_map: &BTreeMap>, - r0: ty::Region<'tcx>) - -> ty::Region<'tcx> { - // Regions that pre-dated the LUB computation stay as they are. - if !is_var_in_set(new_vars, r0) { - assert!(!r0.is_late_bound()); - debug!("generalize_region(r0={:?}): not new variable", r0); - return r0; - } - - let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both()); - - // Variables created during LUB computation which are - // *related* to regions that pre-date the LUB computation - // stay as they are. - if !tainted.iter().all(|&r| is_var_in_set(new_vars, r)) { - debug!("generalize_region(r0={:?}): \ - non-new-variables found in {:?}", - r0, tainted); - assert!(!r0.is_late_bound()); - return r0; - } - - // Otherwise, the variable must be associated with at - // least one of the variables representing bound regions - // in both A and B. Replace the variable with the "first" - // bound region from A that we find it to be associated - // with. - for (a_br, a_r) in a_map { - if tainted.iter().any(|x| x == a_r) { - debug!("generalize_region(r0={:?}): \ - replacing with {:?}, tainted={:?}", - r0, *a_br, tainted); - return infcx.tcx.mk_region(ty::ReLateBound(debruijn, *a_br)); - } - } - - span_bug!( - span, - "region {:?} is not associated with any bound region from A!", - r0) - } - } - - pub fn higher_ranked_glb(&mut self, a: &Binder, b: &Binder, a_is_expected: bool) - -> RelateResult<'tcx, Binder> - where T: Relate<'tcx> - { - debug!("higher_ranked_glb({:?}, {:?})", - a, b); - - // Make a snapshot so we can examine "all bindings that were - // created as part of this type comparison". - return self.infcx.commit_if_ok(|snapshot| { - // Instantiate each bound region with a fresh region variable. - let (a_with_fresh, a_map) = - self.infcx.replace_late_bound_regions_with_fresh_var( - self.trace.cause.span, HigherRankedType, a); - let (b_with_fresh, b_map) = - self.infcx.replace_late_bound_regions_with_fresh_var( - self.trace.cause.span, HigherRankedType, b); - let a_vars = var_ids(self, &a_map); - let b_vars = var_ids(self, &b_map); - - // Collect constraints. - let result0 = - self.glb(a_is_expected).relate(&a_with_fresh, &b_with_fresh)?; - let result0 = - self.infcx.resolve_type_vars_if_possible(&result0); - debug!("glb result0 = {:?}", result0); - - // Generalize the regions appearing in result0 if possible - let new_vars = self.infcx.region_vars_confined_to_snapshot(snapshot); - let span = self.trace.cause.span; - let result1 = - fold_regions_in( - self.tcx(), - &result0, - |r, debruijn| generalize_region(self.infcx, span, snapshot, debruijn, - &new_vars, - &a_map, &a_vars, &b_vars, - r)); - - debug!("glb({:?},{:?}) = {:?}", - a, - b, - result1); - - Ok(ty::Binder::bind(result1)) - }); - - fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - span: Span, - snapshot: &CombinedSnapshot<'a, 'tcx>, - debruijn: ty::DebruijnIndex, - new_vars: &[ty::RegionVid], - a_map: &BTreeMap>, - a_vars: &[ty::RegionVid], - b_vars: &[ty::RegionVid], - r0: ty::Region<'tcx>) - -> ty::Region<'tcx> { - if !is_var_in_set(new_vars, r0) { - assert!(!r0.is_late_bound()); - return r0; - } - - let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both()); - - let mut a_r = None; - let mut b_r = None; - let mut only_new_vars = true; - for r in &tainted { - if is_var_in_set(a_vars, *r) { - if a_r.is_some() { - return fresh_bound_variable(infcx, debruijn); - } else { - a_r = Some(*r); - } - } else if is_var_in_set(b_vars, *r) { - if b_r.is_some() { - return fresh_bound_variable(infcx, debruijn); - } else { - b_r = Some(*r); - } - } else if !is_var_in_set(new_vars, *r) { - only_new_vars = false; - } - } - - // NB---I do not believe this algorithm computes - // (necessarily) the GLB. As written it can - // spuriously fail. In particular, if there is a case - // like: |fn(&a)| and fn(fn(&b)), where a and b are - // free, it will return fn(&c) where c = GLB(a,b). If - // however this GLB is not defined, then the result is - // an error, even though something like - // "fn(fn(&X))" where X is bound would be a - // subtype of both of those. - // - // The problem is that if we were to return a bound - // variable, we'd be computing a lower-bound, but not - // necessarily the *greatest* lower-bound. - // - // Unfortunately, this problem is non-trivial to solve, - // because we do not know at the time of computing the GLB - // whether a GLB(a,b) exists or not, because we haven't - // run region inference (or indeed, even fully computed - // the region hierarchy!). The current algorithm seems to - // works ok in practice. - - if a_r.is_some() && b_r.is_some() && only_new_vars { - // Related to exactly one bound variable from each fn: - return rev_lookup(infcx, span, a_map, a_r.unwrap()); - } else if a_r.is_none() && b_r.is_none() { - // Not related to bound variables from either fn: - assert!(!r0.is_late_bound()); - return r0; - } else { - // Other: - return fresh_bound_variable(infcx, debruijn); - } - } - - fn rev_lookup<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - span: Span, - a_map: &BTreeMap>, - r: ty::Region<'tcx>) -> ty::Region<'tcx> - { - for (a_br, a_r) in a_map { - if *a_r == r { - return infcx.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, *a_br)); - } - } - span_bug!( - span, - "could not find original bound region for {:?}", - r); - } - - fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - debruijn: ty::DebruijnIndex) - -> ty::Region<'tcx> { - infcx.borrow_region_constraints().new_bound(infcx.tcx, debruijn) - } - } -} - -fn var_ids<'a, 'gcx, 'tcx>(fields: &CombineFields<'a, 'gcx, 'tcx>, - map: &BTreeMap>) - -> Vec { - map.iter() - .map(|(_, &r)| match *r { - ty::ReVar(r) => { r } - _ => { - span_bug!( - fields.trace.cause.span, - "found non-region-vid: {:?}", - r); - } - }) - .collect() -} - -fn is_var_in_set(new_vars: &[ty::RegionVid], r: ty::Region<'_>) -> bool { - match *r { - ty::ReVar(ref v) => new_vars.iter().any(|x| x == v), - _ => false - } } fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 55c7eef607b..8875b4169dd 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -15,7 +15,6 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; -use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Least upper bound" (common supertype) @@ -76,31 +75,12 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> where T: Relate<'tcx> { debug!("binders(a={:?}, b={:?})", a, b); - let was_error = self.infcx().probe(|_snapshot| { - // Subtle: use a fresh combine-fields here because we recover - // from Err. Doing otherwise could propagate obligations out - // through our `self.obligations` field. - self.infcx() - .combine_fields(self.fields.trace.clone(), self.fields.param_env) - .higher_ranked_lub(a, b, self.a_is_expected) - .is_err() - }); - debug!("binders: was_error={:?}", was_error); // When higher-ranked types are involved, computing the LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - match self.relate_with_variance(ty::Variance::Invariant, a, b) { - Ok(_) => Ok(a.clone()), - Err(err) => { - debug!("binders: error occurred, was_error={:?}", was_error); - if !was_error { - Err(TypeError::OldStyleLUB(Box::new(err))) - } else { - Err(err) - } - } - } + self.relate_with_variance(ty::Variance::Invariant, a, b)?; + Ok(a.clone()) } } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index ed6e372fe76..855983042c0 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -53,8 +53,6 @@ pub enum TypeError<'tcx> { ProjectionMismatched(ExpectedFound), ProjectionBoundsLength(ExpectedFound), ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), - - OldStyleLUB(Box>), } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] @@ -166,9 +164,6 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { report_maybe_different(f, &format!("trait `{}`", values.expected), &format!("trait `{}`", values.found)) } - OldStyleLUB(ref err) => { - write!(f, "{}", err) - } } } } @@ -266,12 +261,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } }, - OldStyleLUB(err) => { - db.note("this was previously accepted by the compiler but has been phased out"); - db.note("for more information, see https://github.com/rust-lang/rust/issues/45852"); - - self.note_and_explain_type_err(db, &err, sp); - } CyclicTy(ty) => { // Watch out for various cases of cyclic types and try to explain. if ty.is_closure() || ty.is_generator() { diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 62827ea20c3..9e2c0ca86c1 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -455,7 +455,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { ProjectionMismatched(x) => ProjectionMismatched(x), ProjectionBoundsLength(x) => ProjectionBoundsLength(x), Sorts(ref x) => return tcx.lift(x).map(Sorts), - OldStyleLUB(ref x) => return tcx.lift(x).map(OldStyleLUB), ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch) }) } @@ -1000,7 +999,6 @@ EnumTypeFoldableImpl! { (ty::error::TypeError::ProjectionBoundsLength)(x), (ty::error::TypeError::Sorts)(x), (ty::error::TypeError::ExistentialMismatch)(x), - (ty::error::TypeError::OldStyleLUB)(x), } } diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.stderr b/src/test/ui/lub-glb/old-lub-glb-hr.stderr index 9b40062bd57..4182b525888 100644 --- a/src/test/ui/lub-glb/old-lub-glb-hr.stderr +++ b/src/test/ui/lub-glb/old-lub-glb-hr.stderr @@ -11,8 +11,6 @@ LL | | }; | = note: expected type `for<'r, 's> fn(&'r u8, &'s u8)` found type `for<'a> fn(&'a u8, &'a u8)` - = note: this was previously accepted by the compiler but has been phased out - = note: for more information, see https://github.com/rust-lang/rust/issues/45852 error: aborting due to previous error diff --git a/src/test/ui/lub-glb/old-lub-glb-object.stderr b/src/test/ui/lub-glb/old-lub-glb-object.stderr index 79442bd108a..bb8cb45eec3 100644 --- a/src/test/ui/lub-glb/old-lub-glb-object.stderr +++ b/src/test/ui/lub-glb/old-lub-glb-object.stderr @@ -11,8 +11,6 @@ LL | | }; | = note: expected type `&dyn for<'a, 'b> Foo<&'a u8, &'b u8>` found type `&dyn for<'a> Foo<&'a u8, &'a u8>` - = note: this was previously accepted by the compiler but has been phased out - = note: for more information, see https://github.com/rust-lang/rust/issues/45852 error: aborting due to previous error