diff --git a/src/doc/unstable-book/src/language-features/member-constraints.md b/src/doc/unstable-book/src/language-features/member-constraints.md new file mode 100644 index 0000000000000..0d11c31aca6e9 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/member-constraints.md @@ -0,0 +1,29 @@ +# `member_constraints` + +The tracking issue for this feature is: [#61977] + +[#61977]: https://github.com/rust-lang/rust/issues/61977 + +------------------------ + +The `member_constraints` feature gate lets you use `impl Trait` syntax with +multiple unrelated lifetime parameters. + +A simple example is: + +```rust +#![feature(member_constraints)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T {} + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { + (x, y) +} + +fn main() { } +``` + +Without the `member_constraints` feature gate, the above example is an +error because both `'a` and `'b` appear in the impl Trait bounds, but +neither outlives the other. diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index b2c7bd73b6812..b508f91e01ebb 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -23,6 +23,7 @@ use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; +use crate::infer::region_constraints::MemberConstraint; use crate::mir::interpret::ConstValue; use rustc_data_structures::indexed_vec::IndexVec; use rustc_macros::HashStable; @@ -189,11 +190,25 @@ pub enum CanonicalTyVarKind { #[derive(Clone, Debug, HashStable)] pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, - pub region_constraints: Vec>, + pub region_constraints: QueryRegionConstraints<'tcx>, pub certainty: Certainty, pub value: R, } +#[derive(Clone, Debug, Default, HashStable)] +pub struct QueryRegionConstraints<'tcx> { + pub outlives: Vec>, + pub member_constraints: Vec>, +} + +impl QueryRegionConstraints<'_> { + /// Represents an empty (trivially true) set of region + /// constraints. + pub fn is_empty(&self) -> bool { + self.outlives.is_empty() && self.member_constraints.is_empty() + } +} + pub type Canonicalized<'tcx, V> = Canonical<'tcx, V>; pub type CanonicalizedQueryResponse<'tcx, T> = @@ -292,7 +307,8 @@ impl<'tcx, V> Canonical<'tcx, V> { } } -pub type QueryRegionConstraint<'tcx> = ty::Binder, Region<'tcx>>>; +pub type QueryOutlivesConstraint<'tcx> = + ty::Binder, Region<'tcx>>>; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Creates a substitution S for the canonical value with fresh @@ -540,6 +556,19 @@ BraceStructLiftImpl! { } where R: Lift<'tcx> } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> { + outlives, member_constraints + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> { + type Lifted = QueryRegionConstraints<'tcx>; + outlives, member_constraints + } +} + impl<'tcx> Index for CanonicalVarValues<'tcx> { type Output = Kind<'tcx>; diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 3e92fed005cd1..79c5538626be1 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -11,7 +11,7 @@ use crate::arena::ArenaAllocatable; use crate::infer::canonical::substitute::substitute_value; use crate::infer::canonical::{ Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, - OriginalQueryValues, QueryRegionConstraint, QueryResponse, + OriginalQueryValues, QueryRegionConstraints, QueryOutlivesConstraint, QueryResponse, }; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxtBuilder; @@ -132,7 +132,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { { self.canonicalize_response(&QueryResponse { var_values: inference_vars, - region_constraints: vec![], + region_constraints: QueryRegionConstraints::default(), certainty: Certainty::Proven, // Ambiguities are OK! value: answer, }) @@ -174,7 +174,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let region_obligations = self.take_registered_region_obligations(); let region_constraints = self.with_region_constraints(|region_constraints| { - make_query_outlives( + make_query_region_constraints( tcx, region_obligations .iter() @@ -222,10 +222,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { mut obligations, } = self.query_response_substitution(cause, param_env, original_values, query_response)?; - obligations.extend(self.query_region_constraints_into_obligations( + obligations.extend(self.query_outlives_constraints_into_obligations( cause, param_env, - &query_response.value.region_constraints, + &query_response.value.region_constraints.outlives, &result_subst, )); @@ -248,9 +248,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// that come out of these queries, which it wants to convert into /// MIR-based constraints and solve. Therefore, it is most /// convenient for the NLL Type Checker to **directly consume** - /// the `QueryRegionConstraint` values that arise from doing a + /// the `QueryOutlivesConstraint` values that arise from doing a /// query. This is contrast to other parts of the compiler, which - /// would prefer for those `QueryRegionConstraint` to be converted + /// would prefer for those `QueryOutlivesConstraint` to be converted /// into the older infcx-style constraints (e.g., calls to /// `sub_regions` or `register_region_obligation`). /// @@ -263,7 +263,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// result. If any errors arise, they are propagated back as an /// `Err` result. /// - In the case of a successful substitution, we will append - /// `QueryRegionConstraint` values onto the + /// `QueryOutlivesConstraint` values onto the /// `output_query_region_constraints` vector for the solver to /// use (if an error arises, some values may also be pushed, but /// they should be ignored). @@ -279,7 +279,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, original_values: &OriginalQueryValues<'tcx>, query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, - output_query_region_constraints: &mut Vec>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> InferResult<'tcx, R> where R: Debug + TypeFoldable<'tcx>, @@ -287,7 +287,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let result_subst = self.query_response_substitution_guess(cause, original_values, query_response); - // Compute `QueryRegionConstraint` values that unify each of + // Compute `QueryOutlivesConstraint` values that unify each of // the original values `v_o` that was canonicalized into a // variable... let mut obligations = vec![]; @@ -306,8 +306,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. if v_o != v_r { output_query_region_constraints + .outlives .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); output_query_region_constraints + .outlives .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); } } @@ -333,12 +335,12 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } // ...also include the other query region constraints from the query. - output_query_region_constraints.extend( - query_response.value.region_constraints.iter().filter_map(|r_c| { + output_query_region_constraints.outlives.extend( + query_response.value.region_constraints.outlives.iter().filter_map(|r_c| { let r_c = substitute_value(self.tcx, &result_subst, r_c); // Screen out `'a: 'a` cases -- we skip the binder here but - // only care the inner values to one another, so they are still at + // only compare the inner values to one another, so they are still at // consistent binding levels. let &ty::OutlivesPredicate(k1, r2) = r_c.skip_binder(); if k1 != r2.into() { @@ -349,6 +351,13 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { }) ); + // ...also include the query member constraints. + output_query_region_constraints.member_constraints.extend( + query_response.value.region_constraints.member_constraints.iter().map(|p_c| { + substitute_value(self.tcx, &result_subst, p_c) + }) + ); + let user_result: R = query_response.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value); @@ -560,11 +569,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Converts the region constraints resulting from a query into an /// iterator of obligations. - fn query_region_constraints_into_obligations<'a>( + fn query_outlives_constraints_into_obligations<'a>( &'a self, cause: &'a ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>], + unsubstituted_region_constraints: &'a [QueryOutlivesConstraint<'tcx>], result_subst: &'a CanonicalVarValues<'tcx>, ) -> impl Iterator> + 'a + Captures<'tcx> { unsubstituted_region_constraints @@ -645,15 +654,16 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Given the region obligations and constraints scraped from the infcx, /// creates query region constraints. -pub fn make_query_outlives<'tcx>( +pub fn make_query_region_constraints<'tcx>( tcx: TyCtxt<'tcx>, outlives_obligations: impl Iterator, ty::Region<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, -) -> Vec> { +) -> QueryRegionConstraints<'tcx> { let RegionConstraintData { constraints, verifys, givens, + member_constraints, } = region_constraints; assert!(verifys.is_empty()); @@ -684,5 +694,5 @@ pub fn make_query_outlives<'tcx>( ) .collect(); - outlives + QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } } diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 65225163a25a4..cbfb048c064a2 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -53,6 +53,7 @@ use crate::infer::{self, SuppressRegionErrors}; use crate::hir; use crate::hir::def_id::DefId; use crate::hir::Node; +use crate::infer::opaque_types; use crate::middle::region; use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::error::TypeError; @@ -375,6 +376,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } } + + RegionResolutionError::MemberConstraintFailure { + opaque_type_def_id, + hidden_ty, + member_region, + span: _, + choice_regions: _, + } => { + let hidden_ty = self.resolve_vars_if_possible(&hidden_ty); + opaque_types::unexpected_hidden_region_diagnostic( + self.tcx, + Some(region_scope_tree), + opaque_type_def_id, + hidden_ty, + member_region, + ).emit(); + } } } } @@ -411,7 +429,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { RegionResolutionError::GenericBoundFailure(..) => true, RegionResolutionError::ConcreteFailure(..) - | RegionResolutionError::SubSupConflict(..) => false, + | RegionResolutionError::SubSupConflict(..) + | RegionResolutionError::MemberConstraintFailure { .. } => false, }; let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { @@ -429,6 +448,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::MemberConstraintFailure { span, .. } => span, }); errors } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 2613f4c7c2ae3..d06c4434b3aaf 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -1,13 +1,20 @@ //! Lexical region resolution. +use crate::hir::def_id::DefId; use crate::infer::region_constraints::Constraint; use crate::infer::region_constraints::GenericKind; +use crate::infer::region_constraints::MemberConstraint; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; use crate::infer::RegionVariableOrigin; use crate::infer::SubregionOrigin; use crate::middle::free_region::RegionRelations; +use crate::ty::fold::TypeFoldable; +use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use crate::ty::{ReLateBound, RePlaceholder, ReScope, ReVar}; +use crate::ty::{Region, RegionVid}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, @@ -15,12 +22,7 @@ use rustc_data_structures::graph::implementation::{ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use smallvec::SmallVec; use std::fmt; -use std::u32; -use crate::ty::fold::TypeFoldable; -use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; -use crate::ty::{ReLateBound, ReScope, RePlaceholder, ReVar}; -use crate::ty::{Region, RegionVid}; +use syntax_pos::Span; mod graphviz; @@ -36,11 +38,7 @@ pub fn resolve<'tcx>( ) -> (LexicalRegionResolutions<'tcx>, Vec>) { debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; - let mut resolver = LexicalResolver { - region_rels, - var_infos, - data, - }; + let mut resolver = LexicalResolver { region_rels, var_infos, data }; let values = resolver.infer_variable_values(&mut errors); (values, errors) } @@ -84,6 +82,17 @@ pub enum RegionResolutionError<'tcx> { SubregionOrigin<'tcx>, Region<'tcx>, ), + + /// Indicates a failure of a `MemberConstraint`. These arise during + /// impl trait processing explicitly -- basically, the impl trait's hidden type + /// included some region that it was not supposed to. + MemberConstraintFailure { + span: Span, + opaque_type_def_id: DefId, + hidden_ty: Ty<'tcx>, + member_region: Region<'tcx>, + choice_regions: Vec>, + }, } struct RegionAndOrigin<'tcx> { @@ -121,7 +130,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let graph = self.construct_graph(); self.expand_givens(&graph); - self.expansion(&mut var_data); + loop { + self.expansion(&mut var_data); + if !self.enforce_member_constraints(&graph, &mut var_data) { + break; + } + } self.collect_errors(&mut var_data, errors); self.collect_var_errors(&var_data, &graph, errors); var_data @@ -136,7 +150,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { LexicalRegionResolutions { error_region: tcx.lifetimes.re_static, - values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()) + values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()), } } @@ -182,6 +196,113 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// Enforce all member constraints and return true if anything + /// changed. See `enforce_member_constraint` for more details. + fn enforce_member_constraints( + &self, + graph: &RegionGraph<'tcx>, + var_values: &mut LexicalRegionResolutions<'tcx>, + ) -> bool { + // Note: we don't use the `any` combinator because we don't + // want to stop at the first constraint that makes a change. + let mut any_changed = false; + for member_constraint in &self.data.member_constraints { + if self.enforce_member_constraint(graph, member_constraint, var_values) { + any_changed = true; + } + } + any_changed + } + + /// Enforce a constraint like + /// + /// ``` + /// 'r member of ['c...] + /// ``` + /// + /// We look for all choice regions from the list `'c...` that: + /// + /// (a) are greater than the current value of `'r` (which is a lower bound) + /// + /// and + /// + /// (b) are compatible with the upper bounds of `'r` that we can + /// find by traversing the graph. + /// + /// From that list, we look for a *minimal* option `'c_min`. If we + /// find one, then we can enforce that `'r: 'c_min`. + fn enforce_member_constraint( + &self, + graph: &RegionGraph<'tcx>, + member_constraint: &MemberConstraint<'tcx>, + var_values: &mut LexicalRegionResolutions<'tcx>, + ) -> bool { + debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint); + + // The constraint is some inference variable (`vid`) which + // must be equal to one of the options. + let member_vid = match member_constraint.member_region { + ty::ReVar(vid) => *vid, + _ => return false, + }; + + // The current value of `vid` is a lower bound LB -- i.e., we + // know that `LB <= vid` must be true. + let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) { + VarValue::ErrorValue => return false, + VarValue::Value(r) => r, + }; + + // Find all the "upper bounds" -- that is, each region `b` such that + // `r0 <= b` must hold. + let (member_upper_bounds, _) = self.collect_concrete_regions( + graph, + member_vid, + OUTGOING, + None, + ); + + // Get an iterator over the *available choice* -- that is, + // each choice region `c` where `lb <= c` and `c <= ub` for all the + // upper bounds `ub`. + debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds); + let mut options = member_constraint.choice_regions.iter().filter(|option| { + self.sub_concrete_regions(member_lower_bound, option) + && member_upper_bounds + .iter() + .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region)) + }); + + // If there is more than one option, we only make a choice if + // there is a single *least* choice -- i.e., some available + // region that is `<=` all the others. + let mut least_choice: ty::Region<'tcx> = match options.next() { + Some(&r) => r, + None => return false, + }; + debug!("enforce_member_constraint: least_choice={:?}", least_choice); + for &option in options { + debug!("enforce_member_constraint: option={:?}", option); + if !self.sub_concrete_regions(least_choice, option) { + if self.sub_concrete_regions(option, least_choice) { + debug!("enforce_member_constraint: new least choice"); + least_choice = option; + } else { + debug!("enforce_member_constraint: no least choice"); + return false; + } + } + } + + debug!("enforce_member_constraint: final least choice = {:?}", least_choice); + if least_choice != member_lower_bound { + *var_values.value_mut(member_vid) = VarValue::Value(least_choice); + true + } else { + false + } + } + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { self.iterate_until_fixed_point("Expansion", |constraint| { debug!("expansion: constraint={:?}", constraint); @@ -196,7 +317,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let b_data = var_values.value_mut(b_vid); let retain = match *b_data { VarValue::Value(ReStatic) | VarValue::ErrorValue => false, - _ => true + _ => true, }; (a_region, b_vid, b_data, retain) } @@ -204,7 +325,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { // These constraints are checked after expansion // is done, in `collect_errors`. - return (false, false) + return (false, false); } }; @@ -226,16 +347,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { match *a_region { // Check if this relationship is implied by a given. - ty::ReEarlyBound(_) | ty::ReFree(_) => if self.data.givens.contains(&(a_region, b_vid)) - { - debug!("given"); - return false; - }, + ty::ReEarlyBound(_) | ty::ReFree(_) => { + if self.data.givens.contains(&(a_region, b_vid)) { + debug!("given"); + return false; + } + } _ => {} } - match *b_data { VarValue::Value(cur_region) => { // Identical scopes can show up quite often, if the fixed point @@ -267,10 +388,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - debug!( - "Expanding value of {:?} from {:?} to {:?}", - b_vid, cur_region, lub - ); + debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub); *b_data = VarValue::Value(lub); return true; @@ -282,6 +400,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// True if `a <= b`, but not defined over inference variables. + fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool { + self.lub_concrete_regions(a, b) == b + } + + /// Returns the smallest region `c` such that `a <= c` and `b <= c`. fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.tcx(); @@ -321,17 +445,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // at least as big as fr.scope". So, we can // reasonably compare free regions and scopes: let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels - .region_scope_tree - .early_free_scope(self.tcx(), br), - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels - .region_scope_tree - .free_scope(self.tcx(), fr), + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { + self.region_rels.region_scope_tree.early_free_scope(self.tcx(), br) + } + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { + self.region_rels.region_scope_tree.free_scope(self.tcx(), fr) + } _ => bug!(), }; - let r_id = self.region_rels - .region_scope_tree - .nearest_common_ancestor(fr_scope, s_id); + let r_id = + self.region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id); if r_id == fr_scope { // if the free region's scope `fr.scope` is bigger than // the scope region `s_id`, then the LUB is the free @@ -352,9 +475,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // The region corresponding to an outer block is a // subtype of the region corresponding to an inner // block. - let lub = self.region_rels - .region_scope_tree - .nearest_common_ancestor(a_id, b_id); + let lub = self.region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); tcx.mk_region(ReScope(lub)) } @@ -365,11 +486,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // For these types, we cannot define any additional // relationship: - (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => if a == b { - a - } else { - tcx.lifetimes.re_static - }, + (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => { + if a == b { + a + } else { + tcx.lifetimes.re_static + } + } } } @@ -382,10 +505,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { errors: &mut Vec>, ) { for (constraint, origin) in &self.data.constraints { - debug!( - "collect_errors: constraint={:?} origin={:?}", - constraint, origin - ); + debug!("collect_errors: constraint={:?} origin={:?}", constraint, origin); match *constraint { Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { // Expansion will ensure that these constraints hold. Ignore. @@ -433,6 +553,25 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + // Check that all member constraints are satisfied. + for member_constraint in &self.data.member_constraints { + let member_region = var_data.normalize(self.tcx(), member_constraint.member_region); + let choice_regions = member_constraint + .choice_regions + .iter() + .map(|&choice_region| var_data.normalize(self.tcx(), choice_region)); + if !choice_regions.clone().any(|choice_region| member_region == choice_region) { + let span = self.tcx().def_span(member_constraint.opaque_type_def_id); + errors.push(RegionResolutionError::MemberConstraintFailure { + span, + opaque_type_def_id: member_constraint.opaque_type_def_id, + hidden_ty: member_constraint.hidden_ty, + member_region, + choice_regions: choice_regions.collect(), + }); + } + } + for verify in &self.data.verifys { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(self.tcx(), verify.region); @@ -483,34 +622,35 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // idea is to report errors that derive from independent // regions of the graph, but not those that derive from // overlapping locations. - let mut dup_vec = vec![u32::MAX; self.num_vars()]; + let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars()); for (node_vid, value) in var_data.values.iter_enumerated() { match *value { VarValue::Value(_) => { /* Inference successful */ } VarValue::ErrorValue => { - /* Inference impossible: this value contains - inconsistent constraints. - - I think that in this case we should report an - error now -- unlike the case above, we can't - wait to see whether the user needs the result - of this variable. The reason is that the mere - existence of this variable implies that the - region graph is inconsistent, whether or not it - is used. - - For example, we may have created a region - variable that is the GLB of two other regions - which do not have a GLB. Even if that variable - is not used, it implies that those two regions - *should* have a GLB. - - At least I think this is true. It may be that - the mere existence of a conflict in a region variable - that is not used is not a problem, so if this rule - starts to create problems we'll have to revisit - this portion of the code and think hard about it. =) */ + // Inference impossible: this value contains + // inconsistent constraints. + // + // I think that in this case we should report an + // error now -- unlike the case above, we can't + // wait to see whether the user needs the result + // of this variable. The reason is that the mere + // existence of this variable implies that the + // region graph is inconsistent, whether or not it + // is used. + // + // For example, we may have created a region + // variable that is the GLB of two other regions + // which do not have a GLB. Even if that variable + // is not used, it implies that those two regions + // *should* have a GLB. + // + // At least I think this is true. It may be that + // the mere existence of a conflict in a region + // variable that is not used is not a problem, so + // if this rule starts to create problems we'll + // have to revisit this portion of the code and + // think hard about it. =) -- nikomatsakis self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); } } @@ -562,16 +702,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn collect_error_for_expanding_node( &self, graph: &RegionGraph<'tcx>, - dup_vec: &mut [u32], + dup_vec: &mut IndexVec>, node_idx: RegionVid, errors: &mut Vec>, ) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. let (mut lower_bounds, lower_dup) = - self.collect_concrete_regions(graph, node_idx, INCOMING, dup_vec); + self.collect_concrete_regions(graph, node_idx, INCOMING, Some(dup_vec)); let (mut upper_bounds, upper_dup) = - self.collect_concrete_regions(graph, node_idx, OUTGOING, dup_vec); + self.collect_concrete_regions(graph, node_idx, OUTGOING, Some(dup_vec)); if lower_dup || upper_dup { return; @@ -604,9 +744,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { }; for upper_bound in &upper_bounds { - if !self.region_rels - .is_subregion_of(effective_lower_bound, upper_bound.region) - { + if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) { let origin = self.var_infos[node_idx].origin.clone(); debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ @@ -643,7 +781,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { graph: &RegionGraph<'tcx>, orig_node_idx: RegionVid, dir: Direction, - dup_vec: &mut [u32], + mut dup_vec: Option<&mut IndexVec>>, ) -> (Vec>, bool) { struct WalkState<'tcx> { set: FxHashSet, @@ -667,23 +805,23 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let node_idx = state.stack.pop().unwrap(); // check whether we've visited this node on some previous walk - if dup_vec[node_idx.index() as usize] == u32::MAX { - dup_vec[node_idx.index() as usize] = orig_node_idx.index() as u32; - } else if dup_vec[node_idx.index() as usize] != orig_node_idx.index() as u32 { - state.dup_found = true; - } + if let Some(dup_vec) = &mut dup_vec { + if dup_vec[node_idx].is_none() { + dup_vec[node_idx] = Some(orig_node_idx); + } else if dup_vec[node_idx] != Some(orig_node_idx) { + state.dup_found = true; + } - debug!( - "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", - orig_node_idx, node_idx - ); + debug!( + "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", + orig_node_idx, node_idx + ); + } process_edges(&self.data, &mut state, graph, node_idx, dir); } - let WalkState { - result, dup_found, .. - } = state; + let WalkState { result, dup_found, .. } = state; return (result, dup_found); fn process_edges<'tcx>( @@ -699,11 +837,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { for (_, edge) in graph.adjacent_edges(source_node_index, dir) { match edge.data { Constraint::VarSubVar(from_vid, to_vid) => { - let opp_vid = if from_vid == source_vid { - to_vid - } else { - from_vid - }; + let opp_vid = if from_vid == source_vid { to_vid } else { from_vid }; if state.set.insert(opp_vid) { state.stack.push(opp_vid); } @@ -726,7 +860,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } fn iterate_until_fixed_point(&self, tag: &str, mut body: F) - where F: FnMut(&Constraint<'tcx>) -> (bool, bool), + where + F: FnMut(&Constraint<'tcx>) -> (bool, bool), { let mut constraints: SmallVec<[_; 16]> = self.data.constraints.keys().collect(); let mut iteration = 0; @@ -760,17 +895,17 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { && self.bound_is_met(b, var_values, generic_ty, min) } - VerifyBound::OutlivedBy(r) => - self.region_rels.is_subregion_of( - min, - var_values.normalize(self.tcx(), r), - ), + VerifyBound::OutlivedBy(r) => { + self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r)) + } - VerifyBound::AnyBound(bs) => bs.iter() - .any(|b| self.bound_is_met(b, var_values, generic_ty, min)), + VerifyBound::AnyBound(bs) => { + bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min)) + } - VerifyBound::AllBounds(bs) => bs.iter() - .all(|b| self.bound_is_met(b, var_values, generic_ty, min)), + VerifyBound::AllBounds(bs) => { + bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min)) + } } } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index eca1ada851814..663acd67dcd83 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -26,6 +26,7 @@ use crate::ty::{FloatVid, IntVid, TyVid, ConstVid}; use crate::util::nodemap::FxHashMap; use errors::DiagnosticBuilder; +use rustc_data_structures::sync::Lrc; use rustc_data_structures::unify as ut; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::BTreeMap; @@ -904,6 +905,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .make_subregion(origin, a, b); } + /// Require that the region `r` be equal to one of the regions in + /// the set `regions`. + pub fn member_constraint( + &self, + opaque_type_def_id: DefId, + definition_span: Span, + hidden_ty: Ty<'tcx>, + region: ty::Region<'tcx>, + in_regions: &Lrc>>, + ) { + debug!("member_constraint({:?} <: {:?})", region, in_regions); + self.borrow_region_constraints() + .member_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions); + } + pub fn subtype_predicate( &self, cause: &ObligationCause<'tcx>, diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 6b6dbd43167fa..f43e3fa0b7787 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -1,16 +1,19 @@ -use rustc_data_structures::fx::FxHashMap; -use syntax_pos::Span; - -use crate::hir::def_id::DefId; use crate::hir; +use crate::hir::def_id::DefId; use crate::hir::Node; -use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::outlives::free_region_map::FreeRegionRelations; +use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; +use crate::middle::region; use crate::traits::{self, PredicateObligation}; -use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind}; use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind}; +use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; +use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; +use errors::DiagnosticBuilder; +use rustc::session::config::nightly_options; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lrc; +use syntax_pos::Span; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -32,6 +35,20 @@ pub struct OpaqueTypeDecl<'tcx> { /// then `substs` would be `['a, T]`. pub substs: SubstsRef<'tcx>, + /// The span of this particular definition of the opaque type. So + /// for example: + /// + /// ``` + /// existential type Foo; + /// fn bar() -> Foo { + /// ^^^ This is the span we are looking for! + /// ``` + /// + /// In cases where the fn returns `(impl Trait, impl Trait)` or + /// other such combinations, the result is currently + /// over-approximated, but better than nothing. + pub definition_span: Span, + /// The type variable that represents the value of the abstract type /// that we require. In other words, after we compile this function, /// we will be created a constraint like: @@ -98,30 +115,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// - `param_env` -- the in-scope parameter environment to be used for /// obligations /// - `value` -- the value within which we are instantiating opaque types + /// - `value_span` -- the span where the value came from, used in error reporting pub fn instantiate_opaque_types>( &self, parent_def_id: DefId, body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, value: &T, + value_span: Span, ) -> InferOk<'tcx, (T, OpaqueTypeMap<'tcx>)> { - debug!("instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \ - param_env={:?})", - value, parent_def_id, body_id, param_env, + debug!( + "instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \ + param_env={:?})", + value, parent_def_id, body_id, param_env, ); let mut instantiator = Instantiator { infcx: self, parent_def_id, body_id, param_env, + value_span, opaque_types: Default::default(), obligations: vec![], }; let value = instantiator.instantiate_opaque_types_in_map(value); - InferOk { - value: (value, instantiator.opaque_types), - obligations: instantiator.obligations, - } + InferOk { value: (value, instantiator.opaque_types), obligations: instantiator.obligations } } /// Given the map `opaque_types` containing the existential `impl @@ -216,13 +234,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// # The Solution /// - /// We make use of the constraint that we *do* have in the `<=` - /// relation. To do that, we find the "minimum" of all the - /// arguments that appear in the substs: that is, some region - /// which is less than all the others. In the case of `Foo1<'a>`, - /// that would be `'a` (it's the only choice, after all). Then we - /// apply that as a least bound to the variables (e.g., `'a <= - /// '0`). + /// We generally prefer to make `<=` constraints, since they + /// integrate best into the region solver. To do that, we find the + /// "minimum" of all the arguments that appear in the substs: that + /// is, some region which is less than all the others. In the case + /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after + /// all). Then we apply that as a least bound to the variables + /// (e.g., `'a <= '0`). /// /// In some cases, there is no minimum. Consider this example: /// @@ -230,8 +248,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } /// ``` /// - /// Here we would report an error, because `'a` and `'b` have no - /// relation to one another. + /// Here we would report a more complex "in constraint", like `'r + /// in ['a, 'b, 'static]` (where `'r` is some regon appearing in + /// the hidden type). + /// + /// # Constrain regions, not the hidden concrete type + /// + /// Note that generating constraints on each region `Rc` is *not* + /// the same as generating an outlives constraint on `Tc` iself. + /// For example, if we had a function like this: + /// + /// ```rust + /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { + /// (x, y) + /// } + /// + /// // Equivalent to: + /// existential type FooReturn<'a, T>: Foo<'a>; + /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } + /// ``` + /// + /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` + /// is an inference variable). If we generated a constraint that + /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- + /// but this is not necessary, because the existential type we + /// create will be allowed to reference `T`. So we only generate a + /// constraint that `'0: 'a`. /// /// # The `free_region_relations` parameter /// @@ -274,6 +316,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + /// See `constrain_opaque_types` for documentation. pub fn constrain_opaque_type>( &self, def_id: DefId, @@ -290,32 +333,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("constrain_opaque_type: concrete_ty={:?}", concrete_ty); - let abstract_type_generics = tcx.generics_of(def_id); + let opaque_type_generics = tcx.generics_of(def_id); let span = tcx.def_span(def_id); // If there are required region bounds, we can use them. if opaque_defn.has_required_region_bounds { let predicates_of = tcx.predicates_of(def_id); - debug!( - "constrain_opaque_type: predicates: {:#?}", - predicates_of, - ); + debug!("constrain_opaque_type: predicates: {:#?}", predicates_of,); let bounds = predicates_of.instantiate(tcx, opaque_defn.substs); debug!("constrain_opaque_type: bounds={:#?}", bounds); let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs); - let required_region_bounds = tcx.required_region_bounds( - opaque_type, - bounds.predicates, - ); + let required_region_bounds = tcx.required_region_bounds(opaque_type, bounds.predicates); debug_assert!(!required_region_bounds.is_empty()); - for region in required_region_bounds { - concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor { - infcx: self, - least_region: region, - span, + for required_region in required_region_bounds { + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| self.sub_regions(infer::CallReturn(span), required_region, r), }); } return; @@ -329,11 +365,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // `['a]` for the first impl trait and `'b` for the // second. let mut least_region = None; - for param in &abstract_type_generics.params { + for param in &opaque_type_generics.params { match param.kind { GenericParamDefKind::Lifetime => {} - _ => continue + _ => continue, } + // Get the value supplied for this region from the substs. let subst_arg = opaque_defn.substs.region_at(param.index as usize); @@ -350,44 +387,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { least_region = Some(subst_arg); } else { // There are two regions (`lr` and - // `subst_arg`) which are not relatable. We can't - // find a best choice. - let context_name = match opaque_defn.origin { - hir::ExistTyOrigin::ExistentialType => "existential type", - hir::ExistTyOrigin::ReturnImplTrait => "impl Trait", - hir::ExistTyOrigin::AsyncFn => "async fn", - }; - let msg = format!("ambiguous lifetime bound in `{}`", context_name); - let mut err = self.tcx - .sess - .struct_span_err(span, &msg); - - let lr_name = lr.to_string(); - let subst_arg_name = subst_arg.to_string(); - let label_owned; - let label = match (&*lr_name, &*subst_arg_name) { - ("'_", "'_") => "the elided lifetimes here do not outlive one another", - _ => { - label_owned = format!( - "neither `{}` nor `{}` outlives the other", - lr_name, - subst_arg_name, - ); - &label_owned - } - }; - err.span_label(span, label); - - if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin { - err.note("multiple unrelated lifetimes are not allowed in \ - `async fn`."); - err.note("if you're using argument-position elided lifetimes, consider \ - switching to a single named lifetime."); - } - err.emit(); - - least_region = Some(self.tcx.mk_region(ty::ReEmpty)); - break; + // `subst_arg`) which are not relatable. We + // can't find a best choice. Therefore, + // instead of creating a single bound like + // `'r: 'a` (which is our preferred choice), + // we will create a "in bound" like `'r in + // ['a, 'b, 'c]`, where `'a..'c` are the + // regions that appear in the impl trait. + return self.generate_member_constraint( + concrete_ty, + opaque_type_generics, + opaque_defn, + def_id, + lr, + subst_arg, + ); } } } @@ -396,13 +410,121 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let least_region = least_region.unwrap_or(tcx.lifetimes.re_static); debug!("constrain_opaque_types: least_region={:?}", least_region); - concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor { - infcx: self, - least_region, - span, + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| self.sub_regions(infer::CallReturn(span), least_region, r), }); } + /// As a fallback, we sometimes generate an "in constraint". For + /// a case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be + /// related, we would generate a constraint `'r in ['a, 'b, + /// 'static]` for each region `'r` that appears in the hidden type + /// (i.e., it must be equal to `'a`, `'b`, or `'static`). + /// + /// `conflict1` and `conflict2` are the two region bounds that we + /// detected which were unrelated. They are used for diagnostics. + fn generate_member_constraint( + &self, + concrete_ty: Ty<'tcx>, + opaque_type_generics: &ty::Generics, + opaque_defn: &OpaqueTypeDecl<'tcx>, + opaque_type_def_id: DefId, + conflict1: ty::Region<'tcx>, + conflict2: ty::Region<'tcx>, + ) { + // For now, enforce a feature gate outside of async functions. + if self.member_constraint_feature_gate( + opaque_defn, + opaque_type_def_id, + conflict1, + conflict2, + ) { + return; + } + + // Create the set of choice regions: each region in the hidden + // type can be equal to any of the region parameters of the + // opaque type definition. + let choice_regions: Lrc>> = Lrc::new( + opaque_type_generics + .params + .iter() + .filter(|param| match param.kind { + GenericParamDefKind::Lifetime => true, + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => false, + }) + .map(|param| opaque_defn.substs.region_at(param.index as usize)) + .chain(std::iter::once(self.tcx.lifetimes.re_static)) + .collect(), + ); + + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| self.member_constraint( + opaque_type_def_id, + opaque_defn.definition_span, + concrete_ty, + r, + &choice_regions, + ), + }); + } + + /// Member constraints are presently feature-gated except for + /// async-await. We expect to lift this once we've had a bit more + /// time. + fn member_constraint_feature_gate( + &self, + opaque_defn: &OpaqueTypeDecl<'tcx>, + opaque_type_def_id: DefId, + conflict1: ty::Region<'tcx>, + conflict2: ty::Region<'tcx>, + ) -> bool { + // If we have `#![feature(member_constraints)]`, no problems. + if self.tcx.features().member_constraints { + return false; + } + + let span = self.tcx.def_span(opaque_type_def_id); + + // Without a feature-gate, we only generate member-constraints for async-await. + let context_name = match opaque_defn.origin { + // No feature-gate required for `async fn`. + hir::ExistTyOrigin::AsyncFn => return false, + + // Otherwise, generate the label we'll use in the error message. + hir::ExistTyOrigin::ExistentialType => "existential type", + hir::ExistTyOrigin::ReturnImplTrait => "impl Trait", + }; + let msg = format!("ambiguous lifetime bound in `{}`", context_name); + let mut err = self.tcx.sess.struct_span_err(span, &msg); + + let conflict1_name = conflict1.to_string(); + let conflict2_name = conflict2.to_string(); + let label_owned; + let label = match (&*conflict1_name, &*conflict2_name) { + ("'_", "'_") => "the elided lifetimes here do not outlive one another", + _ => { + label_owned = format!( + "neither `{}` nor `{}` outlives the other", + conflict1_name, conflict2_name, + ); + &label_owned + } + }; + err.span_label(span, label); + + if nightly_options::is_nightly_build() { + help!(err, + "add #![feature(member_constraints)] to the crate attributes \ + to enable"); + } + + err.emit(); + true + } + /// Given the fully resolved, instantiated type for an opaque /// type, i.e., the value of an inference variable like C1 or C2 /// (*), computes the "definition type" for an abstract type @@ -456,23 +578,98 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, // after producing an error for each of them. - let definition_ty = - instantiated_ty.fold_with(&mut ReverseMapper::new( - self.tcx, - self.is_tainted_by_errors(), - def_id, - map, - instantiated_ty, - )); - debug!( - "infer_opaque_definition_from_instantiation: definition_ty={:?}", - definition_ty - ); + let definition_ty = instantiated_ty.fold_with(&mut ReverseMapper::new( + self.tcx, + self.is_tainted_by_errors(), + def_id, + map, + instantiated_ty, + )); + debug!("infer_opaque_definition_from_instantiation: definition_ty={:?}", definition_ty); definition_ty } } +pub fn unexpected_hidden_region_diagnostic( + tcx: TyCtxt<'tcx>, + region_scope_tree: Option<®ion::ScopeTree>, + opaque_type_def_id: DefId, + hidden_ty: Ty<'tcx>, + hidden_region: ty::Region<'tcx>, +) -> DiagnosticBuilder<'tcx> { + let span = tcx.def_span(opaque_type_def_id); + let mut err = struct_span_err!( + tcx.sess, + span, + E0700, + "hidden type for `impl Trait` captures lifetime that does not appear in bounds", + ); + + // Explain the region we are capturing. + if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region { + // Assuming regionck succeeded (*), we ought to always be + // capturing *some* region from the fn header, and hence it + // ought to be free. So under normal circumstances, we will go + // down this path which gives a decent human readable + // explanation. + // + // (*) if not, the `tainted_by_errors` flag would be set to + // true in any case, so we wouldn't be here at all. + tcx.note_and_explain_free_region( + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } else { + // Ugh. This is a painful case: the hidden region is not one + // that we can easily summarize or explain. This can happen + // in a case like + // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // + // ``` + // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { + // if condition() { a } else { b } + // } + // ``` + // + // Here the captured lifetime is the intersection of `'a` and + // `'b`, which we can't quite express. + + if let Some(region_scope_tree) = region_scope_tree { + // If the `region_scope_tree` is available, this is being + // invoked from the "region inferencer error". We can at + // least report a really cryptic error for now. + tcx.note_and_explain_region( + region_scope_tree, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } else { + // If the `region_scope_tree` is *unavailable*, this is + // being invoked by the code that comes *after* region + // inferencing. This is a bug, as the region inferencer + // ought to have noticed the failed constraint and invoked + // error reporting, which in turn should have prevented us + // from getting trying to infer the hidden type + // completely. + tcx.sess.delay_span_bug( + span, + &format!( + "hidden type captures unexpected lifetime `{:?}` \ + but no region inference failure", + hidden_region, + ), + ); + } + } + + err +} + // Visitor that requires that (almost) all regions in the type visited outlive // `least_region`. We cannot use `push_outlives_components` because regions in // closure signatures are not included in their outlives components. We need to @@ -486,13 +683,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // // We ignore any type parameters because impl trait values are assumed to // capture all the in-scope type parameters. -struct OpaqueTypeOutlivesVisitor<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - least_region: ty::Region<'tcx>, - span: Span, +struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ + tcx: TyCtxt<'tcx>, + op: OP, } -impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> { +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ fn visit_binder>(&mut self, t: &ty::Binder) -> bool { t.skip_binder().visit_with(self); false // keep visiting @@ -503,7 +705,7 @@ impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> { // ignore bound regions, keep visiting ty::ReLateBound(_, _) => false, _ => { - self.infcx.sub_regions(infer::CallReturn(self.span), self.least_region, r); + (self.op)(r); false } } @@ -519,23 +721,23 @@ impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> { ty::Closure(def_id, ref substs) => { // Skip lifetime parameters of the enclosing item(s) - for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) { + for upvar_ty in substs.upvar_tys(def_id, self.tcx) { upvar_ty.visit_with(self); } - substs.closure_sig_ty(def_id, self.infcx.tcx).visit_with(self); + substs.closure_sig_ty(def_id, self.tcx).visit_with(self); } ty::Generator(def_id, ref substs, _) => { // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. - for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) { + for upvar_ty in substs.upvar_tys(def_id, self.tcx) { upvar_ty.visit_with(self); } - substs.return_ty(def_id, self.infcx.tcx).visit_with(self); - substs.yield_ty(def_id, self.infcx.tcx).visit_with(self); + substs.return_ty(def_id, self.tcx).visit_with(self); + substs.yield_ty(def_id, self.tcx).visit_with(self); } _ => { ty.super_visit_with(self); @@ -616,40 +818,17 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { None => { if !self.map_missing_regions_to_empty && !self.tainted_by_errors { if let Some(hidden_ty) = self.hidden_ty.take() { - let span = self.tcx.def_span(self.opaque_type_def_id); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0700, - "hidden type for `impl Trait` captures lifetime that \ - does not appear in bounds", - ); - - // Assuming regionck succeeded, then we must - // be capturing *some* region from the fn - // header, and hence it must be free, so it's - // ok to invoke this fn (which doesn't accept - // all regions, and would ICE if an - // inappropriate region is given). We check - // `is_tainted_by_errors` by errors above, so - // we don't get in here unless regionck - // succeeded. (Note also that if regionck - // failed, then the regions we are attempting - // to map here may well be giving errors - // *because* the constraints were not - // satisfiable.) - self.tcx.note_and_explain_free_region( - &mut err, - &format!("hidden type `{}` captures ", hidden_ty), + unexpected_hidden_region_diagnostic( + self.tcx, + None, + self.opaque_type_def_id, + hidden_ty, r, - "" - ); - - err.emit(); + ).emit(); } } self.tcx.lifetimes.re_empty - }, + } } } @@ -681,8 +860,8 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // during codegen. let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( - |(index, &kind)| { + let substs = + self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -690,16 +869,15 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // ...but not elsewhere. self.fold_kind_normally(kind) } - }, - )); + })); self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs }) } ty::Generator(def_id, substs, movability) => { let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( - |(index, &kind)| { + let substs = + self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -707,8 +885,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // ...but not elsewhere. self.fold_kind_normally(kind) } - }, - )); + })); self.tcx.mk_generator(def_id, ty::GeneratorSubsts { substs }, movability) } @@ -723,6 +900,7 @@ struct Instantiator<'a, 'tcx> { parent_def_id: DefId, body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, + value_span: Span, opaque_types: OpaqueTypeMap<'tcx>, obligations: Vec>, } @@ -773,12 +951,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let parent_def_id = self.parent_def_id; let def_scope_default = || { let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); - parent_def_id == tcx.hir() - .local_def_id_from_hir_id(opaque_parent_hir_id) + parent_def_id + == tcx.hir().local_def_id_from_hir_id(opaque_parent_hir_id) }; - let (in_definition_scope, origin) = - match tcx.hir().find(opaque_hir_id) - { + let (in_definition_scope, origin) = match tcx.hir().find(opaque_hir_id) { Some(Node::Item(item)) => match item.node { // Anonymous `impl Trait` hir::ItemKind::Existential(hir::ExistTy { @@ -847,10 +1023,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let infcx = self.infcx; let tcx = infcx.tcx; - debug!( - "instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", - def_id, substs - ); + debug!("instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", def_id, substs); // Use the same type variable if the exact same opaque type appears more // than once in the return type (e.g., if it's passed to a type alias). @@ -858,41 +1031,35 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { return opaque_defn.concrete_ty; } let span = tcx.def_span(def_id); - let ty_var = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); + let ty_var = infcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); let predicates_of = tcx.predicates_of(def_id); - debug!( - "instantiate_opaque_types: predicates={:#?}", - predicates_of, - ); + debug!("instantiate_opaque_types: predicates={:#?}", predicates_of,); let bounds = predicates_of.instantiate(tcx, substs); debug!("instantiate_opaque_types: bounds={:?}", bounds); let required_region_bounds = tcx.required_region_bounds(ty, bounds.predicates.clone()); - debug!( - "instantiate_opaque_types: required_region_bounds={:?}", - required_region_bounds - ); + debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds); // Make sure that we are in fact defining the *entire* type // (e.g., `existential type Foo: Bar;` needs to be // defined by a function like `fn foo() -> Foo`). - debug!( - "instantiate_opaque_types: param_env={:#?}", - self.param_env, - ); - debug!( - "instantiate_opaque_types: generics={:#?}", - tcx.generics_of(def_id), - ); + debug!("instantiate_opaque_types: param_env={:#?}", self.param_env,); + debug!("instantiate_opaque_types: generics={:#?}", tcx.generics_of(def_id),); + + // Ideally, we'd get the span where *this specific `ty` came + // from*, but right now we just use the span from the overall + // value being folded. In simple cases like `-> impl Foo`, + // these are the same span, but not in cases like `-> (impl + // Foo, impl Bar)`. + let definition_span = self.value_span; self.opaque_types.insert( def_id, OpaqueTypeDecl { substs, + definition_span, concrete_ty: ty_var, has_required_region_bounds: !required_region_bounds.is_empty(), origin, @@ -911,8 +1078,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // Require that the predicate holds for the concrete type. debug!("instantiate_opaque_types: predicate={:?}", predicate); - self.obligations - .push(traits::Obligation::new(cause, self.param_env, predicate)); + self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); } ty_var @@ -950,9 +1116,7 @@ pub fn may_define_existential_type( ); // Named existential types can be defined by any siblings or children of siblings. - let scope = tcx.hir() - .get_defining_scope(opaque_hir_id) - .expect("could not get defining scope"); + let scope = tcx.hir().get_defining_scope(opaque_hir_id).expect("could not get defining scope"); // We walk up the node tree until we hit the root or the scope of the opaque type. while hir_id != scope && hir_id != hir::CRATE_HIR_ID { hir_id = tcx.hir().get_parent_item(hir_id); diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index f2235fe8d6d12..fcb116fce5c9b 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -8,11 +8,14 @@ use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::sync::Lrc; use rustc_data_structures::unify as ut; +use crate::hir::def_id::DefId; use crate::ty::ReStatic; use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{ReLateBound, ReVar}; use crate::ty::{Region, RegionVid}; +use syntax_pos::Span; use std::collections::BTreeMap; use std::{cmp, fmt, mem}; @@ -78,6 +81,11 @@ pub struct RegionConstraintData<'tcx> { /// be a region variable (or neither, as it happens). pub constraints: BTreeMap, SubregionOrigin<'tcx>>, + /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that + /// `R0` must be equal to one of the regions `R1..Rn`. These occur + /// with `impl Trait` quite frequently. + pub member_constraints: Vec>, + /// A "verify" is something that we need to verify after inference /// is done, but which does not directly affect inference in any /// way. @@ -137,6 +145,43 @@ impl Constraint<'_> { } } +/// Requires that `region` must be equal to one of the regions in `choice_regions`. +/// We often denote this using the syntax: +/// +/// ``` +/// R0 member of [O1..On] +/// ``` +#[derive(Debug, Clone, HashStable)] +pub struct MemberConstraint<'tcx> { + /// The `DefId` of the opaque type causing this constraint: used for error reporting. + pub opaque_type_def_id: DefId, + + /// The span where the hidden type was instantiated. + pub definition_span: Span, + + /// The hidden type in which `member_region` appears: used for error reporting. + pub hidden_ty: Ty<'tcx>, + + /// The region `R0`. + pub member_region: Region<'tcx>, + + /// The options `O1..On`. + pub choice_regions: Lrc>>, +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for MemberConstraint<'tcx> { + opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for MemberConstraint<'a> { + type Lifted = MemberConstraint<'tcx>; + opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions + } +} + /// `VerifyGenericBound(T, _, R, RS)`: the parameter type `T` (or /// associated type) must outlive the region `R`. `T` is known to /// outlive `RS`. Therefore, verify that `R <= RS[i]` for some @@ -643,6 +688,30 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } + pub fn member_constraint( + &mut self, + opaque_type_def_id: DefId, + definition_span: Span, + hidden_ty: Ty<'tcx>, + member_region: ty::Region<'tcx>, + choice_regions: &Lrc>>, + ) { + debug!("member_constraint({:?} in {:#?})", member_region, choice_regions); + + if choice_regions.iter().any(|&r| r == member_region) { + return; + } + + self.data.member_constraints.push(MemberConstraint { + opaque_type_def_id, + definition_span, + hidden_ty, + member_region, + choice_regions: choice_regions.clone() + }); + + } + pub fn make_subregion( &mut self, origin: SubregionOrigin<'tcx>, @@ -906,9 +975,13 @@ impl<'tcx> RegionConstraintData<'tcx> { pub fn is_empty(&self) -> bool { let RegionConstraintData { constraints, + member_constraints, verifys, givens, } = self; - constraints.is_empty() && verifys.is_empty() && givens.is_empty() + constraints.is_empty() && + member_constraints.is_empty() && + verifys.is_empty() && + givens.is_empty() } } diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 72550e23460e6..a2a5f3f950c7a 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::traits::query::Fallible; use crate::infer::canonical::query_response; -use crate::infer::canonical::QueryRegionConstraint; +use crate::infer::canonical::QueryRegionConstraints; use std::rc::Rc; use syntax::source_map::DUMMY_SP; use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt}; @@ -39,7 +39,7 @@ where fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { + ) -> Fallible<(Self::Output, Option>>)> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } @@ -62,7 +62,7 @@ where fn scrape_region_constraints<'tcx, R>( infcx: &InferCtxt<'_, 'tcx>, op: impl FnOnce() -> Fallible>, -) -> Fallible<(R, Option>>>)> { +) -> Fallible<(R, Option>>)> { let mut fulfill_cx = TraitEngine::new(infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; @@ -92,7 +92,7 @@ fn scrape_region_constraints<'tcx, R>( let region_constraint_data = infcx.take_and_reset_region_constraints(); - let outlives = query_response::make_query_outlives( + let region_constraints = query_response::make_query_region_constraints( infcx.tcx, region_obligations .iter() @@ -101,9 +101,9 @@ fn scrape_region_constraints<'tcx, R>( ®ion_constraint_data, ); - if outlives.is_empty() { + if region_constraints.is_empty() { Ok((value, None)) } else { - Ok((value, Some(Rc::new(outlives)))) + Ok((value, Some(Rc::new(region_constraints)))) } } diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 4a07a3120f3e8..e2a5cd9670e0c 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -1,6 +1,6 @@ use crate::infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, - QueryRegionConstraint, QueryResponse, + QueryRegionConstraints, QueryResponse, }; use crate::infer::{InferCtxt, InferOk}; use std::fmt; @@ -32,7 +32,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)>; + ) -> Fallible<(Self::Output, Option>>)>; } /// "Query type ops" are type ops that are implemented using a @@ -85,7 +85,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx { fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'tcx>, - output_query_region_constraints: &mut Vec>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> Fallible { if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { return Ok(result); @@ -140,16 +140,16 @@ where fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { - let mut qrc = vec![]; - let r = Q::fully_perform_into(self, infcx, &mut qrc)?; + ) -> Fallible<(Self::Output, Option>>)> { + let mut region_constraints = QueryRegionConstraints::default(); + let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?; // Promote the final query-region-constraints into a // (optional) ref-counted vector: - let opt_qrc = if qrc.is_empty() { + let opt_qrc = if region_constraints.is_empty() { None } else { - Some(Rc::new(qrc)) + Some(Rc::new(region_constraints)) }; Ok((r, opt_qrc)) diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 27cd745c20fcb..28b52dcea80f1 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -15,6 +15,7 @@ use crate::mir::interpret; use std::fmt; use std::rc::Rc; +use std::sync::Arc; impl fmt::Debug for ty::GenericParamDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -348,7 +349,7 @@ impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) tcx.lift(&self.0).and_then(|a| { tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c))) }) - } + } } impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { @@ -378,6 +379,20 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { } } +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { + type Lifted = Rc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Rc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { + type Lifted = Arc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Arc::new) + } +} + impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { type Lifted = Vec; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { @@ -838,6 +853,16 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { } } +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Arc::new((**self).fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { fn super_fold_with>(&self, folder: &mut F) -> Self { let content: T = (**self).fold_with(folder); diff --git a/src/librustc_data_structures/binary_search_util/mod.rs b/src/librustc_data_structures/binary_search_util/mod.rs new file mode 100644 index 0000000000000..32aa1cb6b1d36 --- /dev/null +++ b/src/librustc_data_structures/binary_search_util/mod.rs @@ -0,0 +1,49 @@ +#[cfg(test)] +mod test; + +/// Uses a sorted slice `data: &[E]` as a kind of "multi-map". The +/// `key_fn` extracts a key of type `K` from the data, and this +/// function finds the range of elements that match the key. `data` +/// must have been sorted as if by a call to `sort_by_key` for this to +/// work. +pub fn binary_search_slice(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E] +where + K: Ord, +{ + let mid = match data.binary_search_by_key(key, &key_fn) { + Ok(mid) => mid, + Err(_) => return &[], + }; + + // We get back *some* element with the given key -- so + // search backwards to find the *first* one. + // + // (It'd be more efficient to use a "galloping" search + // here, but it's not really worth it for small-ish + // amounts of data.) + let mut start = mid; + while start > 0 { + if key_fn(&data[start - 1]) == *key { + start -= 1; + } else { + break; + } + } + + // Now search forward to find the *last* one. + // + // (It'd be more efficient to use a "galloping" search + // here, but it's not really worth it for small-ish + // amounts of data.) + let mut end = mid + 1; + let max = data.len(); + while end < max { + if key_fn(&data[end]) == *key { + end += 1; + } else { + break; + } + } + + &data[start..end] +} diff --git a/src/librustc_data_structures/binary_search_util/test.rs b/src/librustc_data_structures/binary_search_util/test.rs new file mode 100644 index 0000000000000..d74febb5c0fc4 --- /dev/null +++ b/src/librustc_data_structures/binary_search_util/test.rs @@ -0,0 +1,23 @@ +use super::*; + +type Element = (usize, &'static str); + +fn test_map() -> Vec { + let mut data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")]; + data.sort_by_key(get_key); + data +} + +fn get_key(data: &Element) -> usize { + data.0 +} + +#[test] +fn binary_search_slice_test() { + let map = test_map(); + assert_eq!(binary_search_slice(&map, get_key, &0), &[(0, "zero")]); + assert_eq!(binary_search_slice(&map, get_key, &1), &[]); + assert_eq!(binary_search_slice(&map, get_key, &3), &[(3, "three-a"), (3, "three-b")]); + assert_eq!(binary_search_slice(&map, get_key, &22), &[(22, "twenty-two")]); + assert_eq!(binary_search_slice(&map, get_key, &23), &[]); +} diff --git a/src/librustc_data_structures/graph/iterate/mod.rs b/src/librustc_data_structures/graph/iterate/mod.rs index c09364b0a5395..5612778ce07ed 100644 --- a/src/librustc_data_structures/graph/iterate/mod.rs +++ b/src/librustc_data_structures/graph/iterate/mod.rs @@ -1,5 +1,6 @@ use super::super::indexed_vec::IndexVec; -use super::{DirectedGraph, WithSuccessors, WithNumNodes}; +use super::{DirectedGraph, WithNumNodes, WithSuccessors}; +use crate::bit_set::BitSet; #[cfg(test)] mod test; @@ -51,3 +52,36 @@ pub fn reverse_post_order( vec.reverse(); vec } + +/// A "depth-first search" iterator for a directed graph. +pub struct DepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + graph: &'graph G, + stack: Vec, + visited: BitSet, +} + +impl DepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + pub fn new(graph: &'graph G, start_node: G::Node) -> Self { + Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) } + } +} + +impl Iterator for DepthFirstSearch<'_, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + type Item = G::Node; + + fn next(&mut self) -> Option { + let DepthFirstSearch { stack, visited, graph } = self; + let n = stack.pop()?; + stack.extend(graph.successors(n).filter(|&m| visited.insert(m))); + Some(n) + } +} diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 3d47b7d49fb96..2787fa3c6b1e4 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -5,6 +5,7 @@ pub mod implementation; pub mod iterate; mod reference; pub mod scc; +pub mod vec_graph; #[cfg(test)] mod test; @@ -17,6 +18,10 @@ pub trait WithNumNodes: DirectedGraph { fn num_nodes(&self) -> usize; } +pub trait WithNumEdges: DirectedGraph { + fn num_edges(&self) -> usize; +} + pub trait WithSuccessors: DirectedGraph where Self: for<'graph> GraphSuccessors<'graph, Item = ::Node>, @@ -25,6 +30,13 @@ where &'graph self, node: Self::Node, ) -> >::Iter; + + fn depth_first_search(&self, from: Self::Node) -> iterate::DepthFirstSearch<'_, Self> + where + Self: WithNumNodes, + { + iterate::DepthFirstSearch::new(self, from) + } } pub trait GraphSuccessors<'graph> { diff --git a/src/librustc_data_structures/graph/scc/mod.rs b/src/librustc_data_structures/graph/scc/mod.rs index 24c5448639e7d..78554cda77b44 100644 --- a/src/librustc_data_structures/graph/scc/mod.rs +++ b/src/librustc_data_structures/graph/scc/mod.rs @@ -4,7 +4,8 @@ //! O(n) time. use crate::fx::FxHashSet; -use crate::graph::{DirectedGraph, WithNumNodes, WithSuccessors}; +use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; +use crate::graph::vec_graph::VecGraph; use crate::indexed_vec::{Idx, IndexVec}; use std::ops::Range; @@ -58,6 +59,49 @@ impl Sccs { pub fn successors(&self, scc: S) -> &[S] { self.scc_data.successors(scc) } + + /// Construct the reverse graph of the SCC graph. + pub fn reverse(&self) -> VecGraph { + VecGraph::new( + self.num_sccs(), + self.all_sccs() + .flat_map(|source| self.successors(source).iter().map(move |&target| { + (target, source) + })) + .collect(), + ) + } +} + +impl DirectedGraph for Sccs { + type Node = S; +} + +impl WithNumNodes for Sccs { + fn num_nodes(&self) -> usize { + self.num_sccs() + } +} + +impl WithNumEdges for Sccs { + fn num_edges(&self) -> usize { + self.scc_data.all_successors.len() + } +} + +impl GraphSuccessors<'graph> for Sccs { + type Item = S; + + type Iter = std::iter::Cloned>; +} + +impl WithSuccessors for Sccs { + fn successors<'graph>( + &'graph self, + node: S + ) -> >::Iter { + self.successors(node).iter().cloned() + } } impl SccData { diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/src/librustc_data_structures/graph/vec_graph/mod.rs new file mode 100644 index 0000000000000..6fb1bb42d2cfd --- /dev/null +++ b/src/librustc_data_structures/graph/vec_graph/mod.rs @@ -0,0 +1,113 @@ +use crate::indexed_vec::{Idx, IndexVec}; +use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; + +#[cfg(test)] +mod test; + +pub struct VecGraph { + /// Maps from a given node to an index where the set of successors + /// for that node starts. The index indexes into the `edges` + /// vector. To find the range for a given node, we look up the + /// start for that node and then the start for the next node + /// (i.e., with an index 1 higher) and get the range between the + /// two. This vector always has an extra entry so that this works + /// even for the max element. + node_starts: IndexVec, + + edge_targets: Vec, +} + +impl VecGraph { + pub fn new( + num_nodes: usize, + mut edge_pairs: Vec<(N, N)>, + ) -> Self { + // Sort the edges by the source -- this is important. + edge_pairs.sort(); + + let num_edges = edge_pairs.len(); + + // Store the *target* of each edge into `edge_targets`. + let edge_targets: Vec = edge_pairs.iter().map(|&(_, target)| target).collect(); + + // Create the *edge starts* array. We are iterating over over + // the (sorted) edge pairs. We maintain the invariant that the + // length of the `node_starts` arary is enough to store the + // current source node -- so when we see that the source node + // for an edge is greater than the current length, we grow the + // edge-starts array by just enough. + let mut node_starts = IndexVec::with_capacity(num_edges); + for (index, &(source, _)) in edge_pairs.iter().enumerate() { + // If we have a list like `[(0, x), (2, y)]`: + // + // - Start out with `node_starts` of `[]` + // - Iterate to `(0, x)` at index 0: + // - Push one entry because `node_starts.len()` (0) is <= the source (0) + // - Leaving us with `node_starts` of `[0]` + // - Iterate to `(2, y)` at index 1: + // - Push one entry because `node_starts.len()` (1) is <= the source (2) + // - Push one entry because `node_starts.len()` (2) is <= the source (2) + // - Leaving us with `node_starts` of `[0, 1, 1]` + // - Loop terminates + while node_starts.len() <= source.index() { + node_starts.push(index); + } + } + + // Pad out the `node_starts` array so that it has `num_nodes + + // 1` entries. Continuing our example above, if `num_nodes` is + // be `3`, we would push one more index: `[0, 1, 1, 2]`. + // + // Interpretation of that vector: + // + // [0, 1, 1, 2] + // ---- range for N=2 + // ---- range for N=1 + // ---- range for N=0 + while node_starts.len() <= num_nodes { + node_starts.push(edge_targets.len()); + } + + assert_eq!(node_starts.len(), num_nodes + 1); + + Self { node_starts, edge_targets } + } + + /// Gets the successors for `source` as a slice. + pub fn successors(&self, source: N) -> &[N] { + let start_index = self.node_starts[source]; + let end_index = self.node_starts[source.plus(1)]; + &self.edge_targets[start_index..end_index] + } +} + +impl DirectedGraph for VecGraph { + type Node = N; +} + +impl WithNumNodes for VecGraph { + fn num_nodes(&self) -> usize { + self.node_starts.len() - 1 + } +} + +impl WithNumEdges for VecGraph { + fn num_edges(&self) -> usize { + self.edge_targets.len() + } +} + +impl GraphSuccessors<'graph> for VecGraph { + type Item = N; + + type Iter = std::iter::Cloned>; +} + +impl WithSuccessors for VecGraph { + fn successors<'graph>( + &'graph self, + node: N + ) -> >::Iter { + self.successors(node).iter().cloned() + } +} diff --git a/src/librustc_data_structures/graph/vec_graph/test.rs b/src/librustc_data_structures/graph/vec_graph/test.rs new file mode 100644 index 0000000000000..97a9bd2ad0b08 --- /dev/null +++ b/src/librustc_data_structures/graph/vec_graph/test.rs @@ -0,0 +1,51 @@ +use super::*; + +fn create_graph() -> VecGraph { + // Create a simple graph + // + // 5 + // | + // V + // 0 --> 1 --> 2 + // | + // v + // 3 --> 4 + // + // 6 + + VecGraph::new( + 7, + vec![ + (0, 1), + (1, 2), + (1, 3), + (3, 4), + (5, 1), + ], + ) +} + +#[test] +fn num_nodes() { + let graph = create_graph(); + assert_eq!(graph.num_nodes(), 7); +} + +#[test] +fn succesors() { + let graph = create_graph(); + assert_eq!(graph.successors(0), &[1]); + assert_eq!(graph.successors(1), &[2, 3]); + assert_eq!(graph.successors(2), &[]); + assert_eq!(graph.successors(3), &[4]); + assert_eq!(graph.successors(4), &[]); + assert_eq!(graph.successors(5), &[1]); + assert_eq!(graph.successors(6), &[]); +} + +#[test] +fn dfs() { + let graph = create_graph(); + let dfs: Vec<_> = graph.depth_first_search(0).collect(); + assert_eq!(dfs, vec![0, 1, 3, 4, 2]); +} diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 635edbb927e5c..b3a810a622d03 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -19,8 +19,11 @@ pub trait Idx: Copy + 'static + Ord + Debug + Hash { fn index(self) -> usize; fn increment_by(&mut self, amount: usize) { - let v = self.index() + amount; - *self = Self::new(v); + *self = self.plus(amount); + } + + fn plus(self, amount: usize) -> Self { + Self::new(self.index() + amount) } } @@ -167,6 +170,14 @@ macro_rules! newtype_index { } } + impl std::ops::Add for $type { + type Output = Self; + + fn add(self, other: usize) -> Self { + Self::new(self.index() + other) + } + } + impl Idx for $type { #[inline] fn new(value: usize) -> Self { diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index a1d7ab8856daa..98c809f7e2595 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -72,6 +72,7 @@ macro_rules! unlikely { pub mod macros; pub mod svh; pub mod base_n; +pub mod binary_search_util; pub mod bit_set; pub mod box_region; pub mod const_cstr; diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index c4b2a5daef89a..1d9e6064c416b 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs @@ -1,6 +1,6 @@ use crate::borrow_check::nll::type_check::Locations; -use crate::borrow_check::nll::constraints::ConstraintIndex; -use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::OutlivesConstraintIndex; +use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint}; use rustc::mir::ConstraintCategory; use rustc::ty::RegionVid; use rustc_data_structures::graph; @@ -12,8 +12,8 @@ use syntax_pos::DUMMY_SP; /// -> R2` or `R2 -> R1` depending on the direction type `D`. crate struct ConstraintGraph { _direction: D, - first_constraints: IndexVec>, - next_constraints: IndexVec>, + first_constraints: IndexVec>, + next_constraints: IndexVec>, } crate type NormalConstraintGraph = ConstraintGraph; @@ -77,13 +77,13 @@ impl ConstraintGraph { /// reporting. crate fn new( direction: D, - set: &ConstraintSet, + set: &OutlivesConstraintSet, num_region_vars: usize, ) -> Self { let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars); - let mut next_constraints = IndexVec::from_elem(None, &set.constraints); + let mut next_constraints = IndexVec::from_elem(None, &set.outlives); - for (idx, constraint) in set.constraints.iter_enumerated().rev() { + for (idx, constraint) in set.outlives.iter_enumerated().rev() { let head = &mut first_constraints[D::start_region(constraint)]; let next = &mut next_constraints[idx]; debug_assert!(next.is_none()); @@ -103,7 +103,7 @@ impl ConstraintGraph { /// and not constraints. crate fn region_graph<'rg>( &'rg self, - set: &'rg ConstraintSet, + set: &'rg OutlivesConstraintSet, static_region: RegionVid, ) -> RegionGraph<'rg, D> { RegionGraph::new(set, self, static_region) @@ -113,7 +113,7 @@ impl ConstraintGraph { crate fn outgoing_edges<'a>( &'a self, region_sup: RegionVid, - constraints: &'a ConstraintSet, + constraints: &'a OutlivesConstraintSet, static_region: RegionVid, ) -> Edges<'a, D> { //if this is the `'static` region and the graph's direction is normal, @@ -142,8 +142,8 @@ impl ConstraintGraph { crate struct Edges<'s, D: ConstraintGraphDirecton> { graph: &'s ConstraintGraph, - constraints: &'s ConstraintSet, - pointer: Option, + constraints: &'s OutlivesConstraintSet, + pointer: Option, next_static_idx: Option, static_region: RegionVid, } @@ -180,7 +180,7 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> { /// reverse) constraint graph. It implements the graph traits and is /// usd for doing the SCC computation. crate struct RegionGraph<'s, D: ConstraintGraphDirecton> { - set: &'s ConstraintSet, + set: &'s OutlivesConstraintSet, constraint_graph: &'s ConstraintGraph, static_region: RegionVid, } @@ -191,7 +191,7 @@ impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> { /// construct SCCs for region inference but also for error /// reporting. crate fn new( - set: &'s ConstraintSet, + set: &'s OutlivesConstraintSet, constraint_graph: &'s ConstraintGraph, static_region: RegionVid, ) -> Self { diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index b1091eb5ac81f..6121ed0cf0d1c 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -1,37 +1,40 @@ +use crate::borrow_check::nll::type_check::Locations; use rustc::mir::ConstraintCategory; use rustc::ty::RegionVid; use rustc_data_structures::graph::scc::Sccs; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use crate::borrow_check::nll::type_check::Locations; - use std::fmt; -use std::ops::Deref; +use std::ops::Index; crate mod graph; +/// A set of NLL region constraints. These include "outlives" +/// constraints of the form `R1: R2`. Each constraint is identified by +/// a unique `OutlivesConstraintIndex` and you can index into the set +/// (`constraint_set[i]`) to access the constraint details. #[derive(Clone, Default)] -crate struct ConstraintSet { - constraints: IndexVec, +crate struct OutlivesConstraintSet { + outlives: IndexVec, } -impl ConstraintSet { +impl OutlivesConstraintSet { crate fn push(&mut self, constraint: OutlivesConstraint) { debug!( - "ConstraintSet::push({:?}: {:?} @ {:?}", + "OutlivesConstraintSet::push({:?}: {:?} @ {:?}", constraint.sup, constraint.sub, constraint.locations ); if constraint.sup == constraint.sub { // 'a: 'a is pretty uninteresting return; } - self.constraints.push(constraint); + self.outlives.push(constraint); } /// Constructs a "normal" graph from the constraint set; the graph makes it /// easy to find the constraints affecting a particular region. /// /// N.B., this graph contains a "frozen" view of the current - /// constraints. Any new constraints added to the `ConstraintSet` + /// constraints. Any new constraints added to the `OutlivesConstraintSet` /// after the graph is built will not be present in the graph. crate fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph { graph::ConstraintGraph::new(graph::Normal, self, num_region_vars) @@ -54,13 +57,17 @@ impl ConstraintSet { let region_graph = &constraint_graph.region_graph(self, static_region); Sccs::new(region_graph) } + + crate fn outlives(&self) -> &IndexVec { + &self.outlives + } } -impl Deref for ConstraintSet { - type Target = IndexVec; +impl Index for OutlivesConstraintSet { + type Output = OutlivesConstraint; - fn deref(&self) -> &Self::Target { - &self.constraints + fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output { + &self.outlives[i] } } @@ -94,8 +101,8 @@ impl fmt::Debug for OutlivesConstraint { } newtype_index! { - pub struct ConstraintIndex { - DEBUG_FORMAT = "ConstraintIndex({})" + pub struct OutlivesConstraintIndex { + DEBUG_FORMAT = "OutlivesConstraintIndex({})" } } diff --git a/src/librustc_mir/borrow_check/nll/member_constraints.rs b/src/librustc_mir/borrow_check/nll/member_constraints.rs new file mode 100644 index 0000000000000..b5e2e111f38e5 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/member_constraints.rs @@ -0,0 +1,235 @@ +use crate::rustc::ty::{self, Ty}; +use rustc::hir::def_id::DefId; +use rustc::infer::region_constraints::MemberConstraint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use std::hash::Hash; +use std::ops::Index; +use syntax_pos::Span; + +/// Compactly stores a set of `R0 member of [R1...Rn]` constraints, +/// indexed by the region `R0`. +crate struct MemberConstraintSet<'tcx, R> +where + R: Copy + Hash + Eq, +{ + /// Stores the first "member" constraint for a given `R0`. This is an + /// index into the `constraints` vector below. + first_constraints: FxHashMap, + + /// Stores the data about each `R0 member of [R1..Rn]` constraint. + /// These are organized into a linked list, so each constraint + /// contains the index of the next constraint with the same `R0`. + constraints: IndexVec>, + + /// Stores the `R1..Rn` regions for *all* sets. For any given + /// constraint, we keep two indices so that we can pull out a + /// slice. + choice_regions: Vec, +} + +/// Represents a `R0 member of [R1..Rn]` constraint +crate struct NllMemberConstraint<'tcx> { + next_constraint: Option, + + /// The opaque type whose hidden type is being inferred. (Used in error reporting.) + crate opaque_type_def_id: DefId, + + /// The span where the hidden type was instantiated. + crate definition_span: Span, + + /// The hidden type in which `R0` appears. (Used in error reporting.) + crate hidden_ty: Ty<'tcx>, + + /// The region `R0`. + crate member_region_vid: ty::RegionVid, + + /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`. + start_index: usize, + + /// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`. + end_index: usize, +} + +newtype_index! { + crate struct NllMemberConstraintIndex { + DEBUG_FORMAT = "MemberConstraintIndex({})" + } +} + +impl Default for MemberConstraintSet<'tcx, ty::RegionVid> { + fn default() -> Self { + Self { + first_constraints: Default::default(), + constraints: Default::default(), + choice_regions: Default::default(), + } + } +} + +impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { + /// Pushes a member constraint into the set. + /// + /// The input member constraint `m_c` is in the form produced by + /// the the `rustc::infer` code. + /// + /// The `to_region_vid` callback fn is used to convert the regions + /// within into `RegionVid` format -- it typically consults the + /// `UniversalRegions` data structure that is known to the caller + /// (but which this code is unaware of). + crate fn push_constraint( + &mut self, + m_c: &MemberConstraint<'tcx>, + mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, + ) { + debug!("push_constraint(m_c={:?})", m_c); + let member_region_vid: ty::RegionVid = to_region_vid(m_c.member_region); + let next_constraint = self.first_constraints.get(&member_region_vid).cloned(); + let start_index = self.choice_regions.len(); + let end_index = start_index + m_c.choice_regions.len(); + debug!("push_constraint: member_region_vid={:?}", member_region_vid); + let constraint_index = self.constraints.push(NllMemberConstraint { + next_constraint, + member_region_vid, + opaque_type_def_id: m_c.opaque_type_def_id, + definition_span: m_c.definition_span, + hidden_ty: m_c.hidden_ty, + start_index, + end_index, + }); + self.first_constraints.insert(member_region_vid, constraint_index); + self.choice_regions.extend(m_c.choice_regions.iter().map(|&r| to_region_vid(r))); + } +} + +impl MemberConstraintSet<'tcx, R1> +where + R1: Copy + Hash + Eq, +{ + /// Remap the "member region" key using `map_fn`, producing a new + /// member constraint set. This is used in the NLL code to map from + /// the original `RegionVid` to an scc index. In some cases, we + /// may have multiple `R1` values mapping to the same `R2` key -- that + /// is ok, the two sets will be merged. + crate fn into_mapped( + self, + mut map_fn: impl FnMut(R1) -> R2, + ) -> MemberConstraintSet<'tcx, R2> + where + R2: Copy + Hash + Eq, + { + // We can re-use most of the original data, just tweaking the + // linked list links a bit. + // + // For example if we had two keys `Ra` and `Rb` that both now + // wind up mapped to the same key `S`, we would append the + // linked list for `Ra` onto the end of the linked list for + // `Rb` (or vice versa) -- this basically just requires + // rewriting the final link from one list to point at the othe + // other (see `append_list`). + + let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self; + + let mut first_constraints2 = FxHashMap::default(); + first_constraints2.reserve(first_constraints.len()); + + for (r1, start1) in first_constraints { + let r2 = map_fn(r1); + if let Some(&start2) = first_constraints2.get(&r2) { + append_list(&mut constraints, start1, start2); + } + first_constraints2.insert(r2, start1); + } + + MemberConstraintSet { + first_constraints: first_constraints2, + constraints, + choice_regions, + } + } +} + +impl MemberConstraintSet<'tcx, R> +where + R: Copy + Hash + Eq, +{ + crate fn all_indices( + &self, + ) -> impl Iterator { + self.constraints.indices() + } + + /// Iterate down the constraint indices associated with a given + /// peek-region. You can then use `choice_regions` and other + /// methods to access data. + crate fn indices( + &self, + member_region_vid: R, + ) -> impl Iterator + '_ { + let mut next = self.first_constraints.get(&member_region_vid).cloned(); + std::iter::from_fn(move || -> Option { + if let Some(current) = next { + next = self.constraints[current].next_constraint; + Some(current) + } else { + None + } + }) + } + + /// Returns the "choice regions" for a given member + /// constraint. This is the `R1..Rn` from a constraint like: + /// + /// ``` + /// R0 member of [R1..Rn] + /// ``` + crate fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { + let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; + &self.choice_regions[*start_index..*end_index] + } +} + +impl<'tcx, R> Index for MemberConstraintSet<'tcx, R> +where + R: Copy + Hash + Eq, +{ + type Output = NllMemberConstraint<'tcx>; + + fn index(&self, i: NllMemberConstraintIndex) -> &NllMemberConstraint<'tcx> { + &self.constraints[i] + } +} + +/// Given a linked list starting at `source_list` and another linked +/// list starting at `target_list`, modify `target_list` so that it is +/// followed by `source_list`. +/// +/// Before: +/// +/// ``` +/// target_list: A -> B -> C -> (None) +/// source_list: D -> E -> F -> (None) +/// ``` +/// +/// After: +/// +/// ``` +/// target_list: A -> B -> C -> D -> E -> F -> (None) +/// ``` +fn append_list( + constraints: &mut IndexVec>, + target_list: NllMemberConstraintIndex, + source_list: NllMemberConstraintIndex, +) { + let mut p = target_list; + loop { + let mut r = &mut constraints[p]; + match r.next_constraint { + Some(q) => p = q, + None => { + r.next_constraint = Some(source_list); + return; + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 5dd7b7452733c..eb63e0de195e5 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -37,6 +37,7 @@ crate mod type_check; mod universal_regions; mod constraints; +mod member_constraints; use self::facts::AllFacts; use self::region_infer::RegionInferenceContext; @@ -129,6 +130,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( placeholder_index_to_region: _, mut liveness_constraints, outlives_constraints, + member_constraints, closure_bounds_mapping, type_tests, } = constraints; @@ -150,6 +152,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( universal_region_relations, body, outlives_constraints, + member_constraints, closure_bounds_mapping, type_tests, liveness_constraints, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs index 4931005a4547a..d4f6ce8801e63 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs @@ -71,7 +71,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - let mut constraints: Vec<_> = self.constraints.iter().collect(); + let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); constraints.sort(); for constraint in &constraints { let OutlivesConstraint { 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 04ff54e9a5e45..9e08961f440f2 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 @@ -1,4 +1,5 @@ use crate::borrow_check::nll::constraints::OutlivesConstraint; +use crate::borrow_check::nll::region_infer::AppliedMemberConstraint; use crate::borrow_check::nll::region_infer::RegionInferenceContext; use crate::borrow_check::nll::type_check::Locations; use crate::borrow_check::nll::universal_regions::DefiningTy; @@ -195,6 +196,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { Trace::NotVisited => { bug!("found unvisited region {:?} on path to {:?}", p, r) } + Trace::FromOutlivesConstraint(c) => { result.push(c); p = c.sup; @@ -211,10 +213,30 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Otherwise, walk over the outgoing constraints and // enqueue any regions we find, keeping track of how we // reached them. + + // A constraint like `'r: 'x` can come from our constraint + // graph. let fr_static = self.universal_regions.fr_static; - for constraint in self.constraint_graph - .outgoing_edges(r, &self.constraints, fr_static) - { + let outgoing_edges_from_graph = self.constraint_graph + .outgoing_edges(r, &self.constraints, fr_static); + + + // But member constraints can also give rise to `'r: 'x` + // edges that were not part of the graph initially, so + // watch out for those. + let outgoing_edges_from_picks = self.applied_member_constraints(r) + .iter() + .map(|&AppliedMemberConstraint { min_choice, member_constraint_index, .. }| { + let p_c = &self.member_constraints[member_constraint_index]; + OutlivesConstraint { + sup: r, + sub: min_choice, + locations: Locations::All(p_c.definition_span), + category: ConstraintCategory::OpaqueType, + } + }); + + for constraint in outgoing_edges_from_graph.chain(outgoing_edges_from_picks) { debug_assert_eq!(constraint.sup, r); let sub_region = constraint.sub; if let Trace::NotVisited = context[sub_region] { @@ -687,7 +709,11 @@ 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 { + crate fn find_sub_region_live_at( + &self, + fr1: RegionVid, + elem: Location, + ) -> RegionVid { debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); self.find_constraint_paths_between_regions(fr1, |r| { // First look for some `r` such that `fr1: r` and `r` is live at `elem` @@ -729,8 +755,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr1: RegionVid, fr2: RegionVid, ) -> (ConstraintCategory, Span) { - let (category, _, span) = - self.best_blame_constraint(body, fr1, |r| self.provides_universal_region(r, fr1, fr2)); + let (category, _, span) = self.best_blame_constraint( + body, + fr1, + |r| self.provides_universal_region(r, fr1, fr2), + ); (category, span) } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs index 0cf8a0d16f622..fdf2af9f44ebc 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs @@ -63,7 +63,7 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> { vids.into() } fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint> { - (&self.regioncx.constraints.raw[..]).into() + (&self.regioncx.constraints.outlives().raw[..]).into() } // Render `a: b` as `a -> b`, indicating the flow 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 a4fa1d98255ba..4e609460c1f70 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1,25 +1,32 @@ use super::universal_regions::UniversalRegions; use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph; -use crate::borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::{ + ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet, +}; +use crate::borrow_check::nll::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; use crate::borrow_check::nll::region_infer::values::{ - PlaceholderIndices, RegionElement, ToElementIndex + PlaceholderIndices, RegionElement, ToElementIndex, }; -use crate::borrow_check::Upvar; use crate::borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations; use crate::borrow_check::nll::type_check::Locations; +use crate::borrow_check::Upvar; use rustc::hir::def_id::DefId; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryOutlivesConstraint; +use rustc::infer::opaque_types; use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc::mir::{ - ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, Body, + Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, + ConstraintCategory, Local, Location, }; use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; +use rustc_data_structures::binary_search_util; use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::graph::WithSuccessors; use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::{Diagnostic, DiagnosticBuilder}; use syntax_pos::Span; @@ -49,17 +56,31 @@ pub struct RegionInferenceContext<'tcx> { liveness_constraints: LivenessValues, /// The outlives constraints computed by the type-check. - constraints: Rc, + constraints: Rc, /// The constraint-set, but in graph form, making it easy to traverse /// the constraints adjacent to a particular region. Used to construct /// the SCC (see `constraint_sccs`) and for error reporting. constraint_graph: Rc, - /// The SCC computed from `constraints` and the constraint graph. Used to + /// The SCC computed from `constraints` and the constraint + /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to /// compute the values of each region. constraint_sccs: Rc>, + /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` + /// exists if `B: A`. Computed lazilly. + rev_constraint_graph: Option>>, + + /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. + member_constraints: Rc>, + + /// Records the member constraints that we applied to each scc. + /// This is useful for error reporting. Once constraint + /// propagation is done, this vector is sorted according to + /// `member_region_scc`. + member_constraints_applied: Vec, + /// Map closure bounds to a `Span` that should be used for error reporting. closure_bounds_mapping: FxHashMap>, @@ -95,6 +116,32 @@ pub struct RegionInferenceContext<'tcx> { universal_region_relations: Rc>, } +/// Each time that `apply_member_constraint` is successful, it appends +/// one of these structs to the `member_constraints_applied` field. +/// This is used in error reporting to trace out what happened. +/// +/// The way that `apply_member_constraint` works is that it effectively +/// adds a new lower bound to the SCC it is analyzing: so you wind up +/// with `'R: 'O` where `'R` is the pick-region and `'O` is the +/// minimal viable option. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +struct AppliedMemberConstraint { + /// The SCC that was affected. (The "member region".) + /// + /// The vector if `AppliedMemberConstraint` elements is kept sorted + /// by this field. + member_region_scc: ConstraintSccIndex, + + /// The "best option" that `apply_member_constraint` found -- this was + /// added as an "ad-hoc" lower-bound to `member_region_scc`. + min_choice: ty::RegionVid, + + /// The "member constraint index" -- we can find out details about + /// the constraint from + /// `set.member_constraints[member_constraint_index]`. + member_constraint_index: NllMemberConstraintIndex, +} + struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential /// variable? etc. (See the `NLLRegionVariableOrigin` for more @@ -186,7 +233,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { placeholder_indices: Rc, universal_region_relations: Rc>, _body: &Body<'tcx>, - outlives_constraints: ConstraintSet, + outlives_constraints: OutlivesConstraintSet, + member_constraints_in: MemberConstraintSet<'tcx, RegionVid>, closure_bounds_mapping: FxHashMap< Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>, @@ -218,12 +266,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); + let member_constraints = + Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); + let mut result = Self { definitions, liveness_constraints, constraints, constraint_graph, constraint_sccs, + rev_constraint_graph: None, + member_constraints, + member_constraints_applied: Vec::new(), closure_bounds_mapping, scc_universes, scc_representatives, @@ -341,9 +395,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!( "init_free_and_bound_regions: placeholder {:?} is \ not compatible with universe {:?} of its SCC {:?}", - placeholder, - scc_universe, - scc, + placeholder, scc_universe, scc, ); self.add_incompatible_universe(scc); } @@ -394,6 +446,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_universes[scc] } + /// Once region solving has completed, this function will return + /// the member constraints that were applied to the value of a given + /// region `r`. See `AppliedMemberConstraint`. + fn applied_member_constraints(&self, r: impl ToRegionVid) -> &[AppliedMemberConstraint] { + let scc = self.constraint_sccs.scc(r.to_region_vid()); + binary_search_util::binary_search_slice( + &self.member_constraints_applied, + |applied| applied.member_region_scc, + &scc, + ) + } + /// Performs region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -428,11 +492,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // to store those. Otherwise, we'll pass in `None` to the // functions below, which will trigger them to report errors // eagerly. - let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) { - Some(vec![]) - } else { - None - }; + let mut outlives_requirements = + if infcx.tcx.is_closure(mir_def_id) { Some(vec![]) } else { None }; self.check_type_tests( infcx, @@ -451,16 +512,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer, ); + self.check_member_constraints(infcx, mir_def_id, errors_buffer); + let outlives_requirements = outlives_requirements.unwrap_or(vec![]); if outlives_requirements.is_empty() { None } else { let num_external_vids = self.universal_regions.num_global_and_external_regions(); - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements, - }) + Some(ClosureRegionRequirements { num_external_vids, outlives_requirements }) } } @@ -472,7 +532,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("propagate_constraints()"); debug!("propagate_constraints: constraints={:#?}", { - let mut constraints: Vec<_> = self.constraints.iter().collect(); + let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); constraints.sort(); constraints .into_iter() @@ -488,8 +548,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { for scc_index in self.constraint_sccs.all_sccs() { self.propagate_constraint_sccs_if_new(scc_index, visited); } + + // Sort the applied member constraints so we can binary search + // through them later. + self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc); } + /// Computes the value of the SCC `scc_a` if it has not already + /// been computed. The `visited` parameter is a bitset #[inline] fn propagate_constraint_sccs_if_new( &mut self, @@ -501,6 +567,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + /// Computes the value of the SCC `scc_a`, which has not yet been + /// computed. This works by first computing all successors of the + /// SCC (if they haven't been computed already) and then unioning + /// together their elements. fn propagate_constraint_sccs_new( &mut self, scc_a: ConstraintSccIndex, @@ -510,10 +580,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Walk each SCC `B` such that `A: B`... for &scc_b in constraint_sccs.successors(scc_a) { - debug!( - "propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", - scc_a, scc_b - ); + debug!("propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", scc_a, scc_b); // ...compute the value of `B`... self.propagate_constraint_sccs_if_new(scc_b, visited); @@ -531,6 +598,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + // Now take member constraints into account. + let member_constraints = self.member_constraints.clone(); + for m_c_i in member_constraints.indices(scc_a) { + self.apply_member_constraint( + scc_a, + m_c_i, + member_constraints.choice_regions(m_c_i), + ); + } + debug!( "propagate_constraint_sccs: scc_a = {:?} has value {:?}", scc_a, @@ -538,6 +615,167 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } + /// Invoked for each `R0 member of [R1..Rn]` constraint. + /// + /// `scc` is the SCC containing R0, and `choice_regions` are the + /// `R1..Rn` regions -- they are always known to be universal + /// regions (and if that's not true, we just don't attempt to + /// enforce the constraint). + /// + /// The current value of `scc` at the time the method is invoked + /// is considered a *lower bound*. If possible, we will modify + /// the constraint to set it equal to one of the option regions. + /// If we make any changes, returns true, else false. + fn apply_member_constraint( + &mut self, + scc: ConstraintSccIndex, + member_constraint_index: NllMemberConstraintIndex, + choice_regions: &[ty::RegionVid], + ) -> bool { + debug!("apply_member_constraint(scc={:?}, choice_regions={:#?})", scc, choice_regions,); + + if let Some(uh_oh) = + choice_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r)) + { + // FIXME(#61773): This case can only occur with + // `impl_trait_in_bindings`, I believe, and we are just + // opting not to handle it for now. See #61773 for + // details. + bug!( + "member constraint for `{:?}` has an option region `{:?}` \ + that is not a universal region", + self.member_constraints[member_constraint_index].opaque_type_def_id, + uh_oh, + ); + } + + // Create a mutable vector of the options. We'll try to winnow + // them down. + let mut choice_regions: Vec = choice_regions.to_vec(); + + // The 'member region' in a member constraint is part of the + // hidden type, which must be in the root universe. Therefore, + // it cannot have any placeholders in its value. + assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); + debug_assert!( + self.scc_values.placeholders_contained_in(scc).next().is_none(), + "scc {:?} in a member constraint has placeholder value: {:?}", + scc, + self.scc_values.region_value_str(scc), + ); + + // The existing value for `scc` is a lower-bound. This will + // consist of some set `{P} + {LB}` of points `{P}` and + // lower-bound free regions `{LB}`. As each choice region `O` + // is a free region, it will outlive the points. But we can + // only consider the option `O` if `O: LB`. + choice_regions.retain(|&o_r| { + self.scc_values + .universal_regions_outlived_by(scc) + .all(|lb| self.universal_region_relations.outlives(o_r, lb)) + }); + debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions); + + // Now find all the *upper bounds* -- that is, each UB is a + // free region that must outlive the member region `R0` (`UB: + // R0`). Therefore, we need only keep an option `O` if `UB: O` + // for all UB. + if choice_regions.len() > 1 { + let universal_region_relations = self.universal_region_relations.clone(); + let rev_constraint_graph = self.rev_constraint_graph(); + for ub in self.upper_bounds(scc, &rev_constraint_graph) { + debug!("apply_member_constraint: ub={:?}", ub); + choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); + } + debug!("apply_member_constraint: after ub, choice_regions={:?}", choice_regions); + } + + // If we ruled everything out, we're done. + if choice_regions.is_empty() { + return false; + } + + // Otherwise, we need to find the minimum remaining choice, if + // any, and take that. + debug!("apply_member_constraint: choice_regions remaining are {:#?}", choice_regions); + let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option { + let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); + let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); + if r1_outlives_r2 && r2_outlives_r1 { + Some(r1.min(r2)) + } else if r1_outlives_r2 { + Some(r2) + } else if r2_outlives_r1 { + Some(r1) + } else { + None + } + }; + let mut min_choice = choice_regions[0]; + for &other_option in &choice_regions[1..] { + debug!( + "apply_member_constraint: min_choice={:?} other_option={:?}", + min_choice, other_option, + ); + match min(min_choice, other_option) { + Some(m) => min_choice = m, + None => { + debug!( + "apply_member_constraint: {:?} and {:?} are incomparable; no min choice", + min_choice, other_option, + ); + return false; + } + } + } + + let min_choice_scc = self.constraint_sccs.scc(min_choice); + debug!( + "apply_member_constraint: min_choice={:?} best_choice_scc={:?}", + min_choice, + min_choice_scc, + ); + if self.scc_values.add_region(scc, min_choice_scc) { + self.member_constraints_applied.push(AppliedMemberConstraint { + member_region_scc: scc, + min_choice, + member_constraint_index, + }); + + true + } else { + false + } + } + + /// Compute and return the reverse SCC-based constraint graph (lazilly). + fn upper_bounds( + &'a mut self, + scc0: ConstraintSccIndex, + rev_constraint_graph: &'a VecGraph, + ) -> impl Iterator + 'a { + let scc_values = &self.scc_values; + let mut duplicates = FxHashSet::default(); + rev_constraint_graph + .depth_first_search(scc0) + .skip(1) + .flat_map(move |scc1| scc_values.universal_regions_outlived_by(scc1)) + .filter(move |&r| duplicates.insert(r)) + } + + /// Compute and return the reverse SCC-based constraint graph (lazilly). + fn rev_constraint_graph( + &mut self, + ) -> Rc> { + if let Some(g) = &self.rev_constraint_graph { + return g.clone(); + } + + let rev_graph = Rc::new(self.constraint_sccs.reverse()); + self.rev_constraint_graph = Some(rev_graph.clone()); + rev_graph + } + /// Returns `true` if all the elements in the value of `scc_b` are nameable /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. @@ -554,9 +792,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Otherwise, we have to iterate over the universe elements in // B's value, and check whether all of them are nameable // from universe_a - self.scc_values - .placeholders_contained_in(scc_b) - .all(|p| universe_a.can_name(p.universe)) + self.scc_values.placeholders_contained_in(scc_b).all(|p| universe_a.can_name(p.universe)) } /// Extend `scc` so that it can outlive some placeholder region @@ -731,12 +967,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> bool { let tcx = infcx.tcx; - let TypeTest { - generic_kind, - lower_bound, - locations, - verify_bound: _, - } = type_test; + let TypeTest { generic_kind, lower_bound, locations, verify_bound: _ } = type_test; let generic_ty = generic_kind.to_ty(tcx); let subject = match self.try_promote_type_test_subject(infcx, generic_ty) { @@ -886,11 +1117,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// except that it converts further takes the non-local upper /// bound of `'y`, so that the final result is non-local. fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!( - "non_local_universal_upper_bound(r={:?}={})", - r, - self.region_value_str(r) - ); + debug!("non_local_universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); let lub = self.universal_upper_bound(r); @@ -898,10 +1125,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // creator. let non_local_lub = self.universal_region_relations.non_local_upper_bound(lub); - debug!( - "non_local_universal_upper_bound: non_local_lub={:?}", - non_local_lub - ); + debug!("non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub); non_local_lub } @@ -921,11 +1145,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!( - "universal_upper_bound(r={:?}={})", - r, - self.region_value_str(r) - ); + debug!("universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); // Find the smallest universal region that contains all other // universal regions within `region`. @@ -950,10 +1170,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { lower_bound: RegionVid, verify_bound: &VerifyBound<'tcx>, ) -> bool { - debug!( - "eval_verify_bound(lower_bound={:?}, verify_bound={:?})", - lower_bound, verify_bound - ); + debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); match verify_bound { VerifyBound::IfEq(test_ty, verify_bound1) => { @@ -962,7 +1179,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { VerifyBound::OutlivedBy(r) => { let r_vid = self.to_region_vid(r); - self.eval_outlives(body, r_vid, lower_bound) + self.eval_outlives(r_vid, lower_bound) } VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { @@ -1035,22 +1252,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) } - // Evaluate whether `sup_region: sub_region @ point`. - fn eval_outlives( - &self, - _body: &Body<'tcx>, - sup_region: RegionVid, - sub_region: RegionVid, - ) -> bool { + // Evaluate whether `sup_region == sub_region`. + fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool { + self.eval_outlives(r1, r2) && self.eval_outlives(r2, r1) + } + + // Evaluate whether `sup_region: sub_region`. + fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { debug!("eval_outlives({:?}: {:?})", sup_region, sub_region); debug!( - "eval_outlives: sup_region's value = {:?}", + "eval_outlives: sup_region's value = {:?} universal={:?}", self.region_value_str(sup_region), + self.universal_regions.is_universal_region(sup_region), ); debug!( - "eval_outlives: sub_region's value = {:?}", + "eval_outlives: sub_region's value = {:?} universal={:?}", self.region_value_str(sub_region), + self.universal_regions.is_universal_region(sub_region), ); let sub_region_scc = self.constraint_sccs.scc(sub_region); @@ -1062,9 +1281,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // now). Therefore, the sup-region outlives the sub-region if, // for each universal region R1 in the sub-region, there // exists some region R2 in the sup-region that outlives R1. - let universal_outlives = self.scc_values - .universal_regions_outlived_by(sub_region_scc) - .all(|r1| { + let universal_outlives = + self.scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| { self.scc_values .universal_regions_outlived_by(sup_region_scc) .any(|r2| self.universal_region_relations.outlives(r2, r1)) @@ -1082,8 +1300,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { return true; } - self.scc_values - .contains_points(sup_region_scc, sub_region_scc) + self.scc_values.contains_points(sup_region_scc, sub_region_scc) } /// Once regions have been propagated, this method is used to see @@ -1165,12 +1382,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Because this free region must be in the ROOT universe, we // know it cannot contain any bound universes. assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT); - debug_assert!( - self.scc_values - .placeholders_contained_in(longer_fr_scc) - .next() - .is_none() - ); + debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none()); // Only check all of the relations for the main representative of each // SCC, otherwise just check that we outlive said representative. This @@ -1224,9 +1436,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer: &mut Vec, ) -> Option { // If it is known that `fr: o`, carry on. - if self.universal_region_relations - .outlives(longer_fr, shorter_fr) - { + if self.universal_region_relations.outlives(longer_fr, shorter_fr) { return None; } @@ -1240,9 +1450,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // We'll call it `fr-` -- it's ever so slightly smaller than // `longer_fr`. - if let Some(fr_minus) = self - .universal_region_relations - .non_local_lower_bound(longer_fr) + if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) { debug!("check_universal_region: fr_minus={:?}", fr_minus); @@ -1252,12 +1460,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Grow `shorter_fr` until we find some non-local regions. (We // always will.) We'll call them `shorter_fr+` -- they're ever // so slightly larger than `shorter_fr`. - let shorter_fr_plus = self.universal_region_relations - .non_local_upper_bounds(&shorter_fr); - debug!( - "check_universal_region: shorter_fr_plus={:?}", - shorter_fr_plus - ); + let shorter_fr_plus = + self.universal_region_relations.non_local_upper_bounds(&shorter_fr); + debug!("check_universal_region: shorter_fr_plus={:?}", shorter_fr_plus); for &&fr in &shorter_fr_plus { // Push the constraint `fr-: shorter_fr+` propagated_outlives_requirements.push(ClosureOutlivesRequirement { @@ -1289,28 +1494,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { longer_fr: RegionVid, placeholder: ty::PlaceholderRegion, ) { - debug!( - "check_bound_universal_region(fr={:?}, placeholder={:?})", - longer_fr, placeholder, - ); + debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,); let longer_fr_scc = self.constraint_sccs.scc(longer_fr); - debug!( - "check_bound_universal_region: longer_fr_scc={:?}", - longer_fr_scc, - ); + 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 // else about it! let error_element = match { - self.scc_values - .elements_contained_in(longer_fr_scc) - .find(|element| match element { - RegionElement::Location(_) => true, - RegionElement::RootUniversalRegion(_) => true, - RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1, - }) + self.scc_values.elements_contained_in(longer_fr_scc).find(|element| match element { + RegionElement::Location(_) => true, + RegionElement::RootUniversalRegion(_) => true, + RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1, + }) } { Some(v) => v, None => return, @@ -1321,7 +1518,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let error_region = match error_element { RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), RegionElement::RootUniversalRegion(r) => r, - RegionElement::PlaceholderRegion(error_placeholder) => self.definitions + RegionElement::PlaceholderRegion(error_placeholder) => self + .definitions .iter_enumerated() .filter_map(|(r, definition)| match definition.origin { NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), @@ -1339,12 +1537,50 @@ impl<'tcx> RegionInferenceContext<'tcx> { // the AST-based checker uses a more conservative check, // so to even see this error, one must pass in a special // flag. - let mut diag = infcx - .tcx - .sess - .struct_span_err(span, "higher-ranked subtype error"); + let mut diag = infcx.tcx.sess.struct_span_err(span, "higher-ranked subtype error"); diag.emit(); } + + fn check_member_constraints( + &self, + infcx: &InferCtxt<'_, 'tcx>, + mir_def_id: DefId, + errors_buffer: &mut Vec, + ) { + let member_constraints = self.member_constraints.clone(); + for m_c_i in member_constraints.all_indices() { + debug!("check_member_constraint(m_c_i={:?})", m_c_i); + let m_c = &member_constraints[m_c_i]; + let member_region_vid = m_c.member_region_vid; + debug!( + "check_member_constraint: member_region_vid={:?} with value {}", + member_region_vid, + self.region_value_str(member_region_vid), + ); + let choice_regions = member_constraints.choice_regions(m_c_i); + debug!("check_member_constraint: choice_regions={:?}", choice_regions); + + // Did the member region wind up equal to any of the option regions? + if let Some(o) = choice_regions.iter().find(|&&o_r| { + self.eval_equal(o_r, m_c.member_region_vid) + }) { + debug!("check_member_constraint: evaluated as equal to {:?}", o); + continue; + } + + // If not, report an error. + let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id); + let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid)); + opaque_types::unexpected_hidden_region_diagnostic( + infcx.tcx, + Some(region_scope_tree), + m_c.opaque_type_def_id, + m_c.hidden_ty, + member_region, + ) + .buffer(errors_buffer); + } + } } impl<'tcx> RegionDefinition<'tcx> { @@ -1358,11 +1594,7 @@ impl<'tcx> RegionDefinition<'tcx> { _ => NLLRegionVariableOrigin::Existential, }; - Self { - origin, - universe, - external_name: None, - } + Self { origin, universe, external_name: None } } } @@ -1372,7 +1604,7 @@ pub trait ClosureRegionRequirementsExt<'tcx> { tcx: TyCtxt<'tcx>, closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, - ) -> Vec>; + ) -> Vec>; fn subst_closure_mapping( &self, @@ -1402,7 +1634,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx tcx: TyCtxt<'tcx>, closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, - ) -> Vec> { + ) -> Vec> { debug!( "apply_requirements(closure_def_id={:?}, closure_substs={:?})", closure_def_id, closure_substs @@ -1465,10 +1697,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx if let ty::ReClosureBound(vid) = r { closure_mapping[*vid] } else { - bug!( - "subst_closure_mapping: encountered non-closure bound free region {:?}", - r - ) + bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) } }) } 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 77a4d2699fff7..8de014522dea7 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 @@ -3,7 +3,8 @@ use crate::borrow_check::nll::region_infer::TypeTest; use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use crate::borrow_check::nll::universal_regions::UniversalRegions; use crate::borrow_check::nll::ToRegionVid; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryRegionConstraints; +use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc::infer::region_constraints::{GenericKind, VerifyBound}; @@ -49,13 +50,33 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } - pub(super) fn convert_all(&mut self, query_constraints: &[QueryRegionConstraint<'tcx>]) { - for query_constraint in query_constraints { + pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { + debug!("convert_all(query_constraints={:#?})", query_constraints); + + let QueryRegionConstraints { outlives, member_constraints } = query_constraints; + + // Annoying: to invoke `self.to_region_vid`, we need access to + // `self.constraints`, but we also want to be mutating + // `self.member_constraints`. For now, just swap out the value + // we want and replace at the end. + let mut tmp = std::mem::replace( + &mut self.constraints.member_constraints, + Default::default(), + ); + for member_constraint in member_constraints { + tmp.push_constraint( + member_constraint, + |r| self.to_region_vid(r), + ); + } + self.constraints.member_constraints = tmp; + + for query_constraint in outlives { self.convert(query_constraint); } } - pub(super) fn convert(&mut self, query_constraint: &QueryRegionConstraint<'tcx>) { + pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) { debug!("generate: constraints at: {:#?}", self.locations); // Extract out various useful fields we'll need below. 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 1bb3acc28f0c8..d18a8e87453a5 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 @@ -2,7 +2,7 @@ use crate::borrow_check::nll::type_check::constraint_conversion; use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use crate::borrow_check::nll::universal_regions::UniversalRegions; use crate::borrow_check::nll::ToRegionVid; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::infer::outlives::free_region_map::FreeRegionRelations; use rustc::infer::region_constraints::GenericKind; use rustc::infer::InferCtxt; @@ -287,7 +287,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { self.relations.relate_universal_regions(fr, fr_fn_body); } - for data in constraint_sets { + for data in &constraint_sets { constraint_conversion::ConstraintConversion::new( self.infcx, &self.universal_regions, @@ -297,7 +297,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { Locations::All(DUMMY_SP), ConstraintCategory::Internal, &mut self.constraints, - ).convert_all(&data); + ).convert_all(data); } CreateResult { @@ -311,7 +311,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come /// from this local. - fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>>> { + fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>> { debug!("add_implied_bounds(ty={:?})", ty); let (bounds, constraints) = self.param_env diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs index 3b138bc126257..4af78fa5e0f42 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs @@ -1,5 +1,5 @@ use crate::borrow_check::location::LocationTable; -use crate::borrow_check::nll::constraints::ConstraintSet; +use crate::borrow_check::nll::constraints::OutlivesConstraintSet; use crate::borrow_check::nll::facts::{AllFacts, AllFactsExt}; use crate::borrow_check::nll::region_infer::values::RegionValueElements; use crate::borrow_check::nll::universal_regions::UniversalRegions; @@ -107,7 +107,7 @@ fn compute_live_locals( fn regions_that_outlive_free_regions( num_region_vars: usize, universal_regions: &UniversalRegions<'tcx>, - constraint_set: &ConstraintSet, + constraint_set: &OutlivesConstraintSet, ) -> FxHashSet { // Build a graph of the outlives constraints thus far. This is // a reverse graph, so for each constraint `R1: R2` we have an diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs index f1d568f0cf24c..f160f658f5576 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs @@ -6,7 +6,7 @@ use crate::borrow_check::nll::type_check::TypeChecker; use crate::dataflow::indexes::MovePathIndex; use crate::dataflow::move_paths::MoveData; use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces}; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, Body}; use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::traits::query::type_op::outlives::DropckOutlives; @@ -88,7 +88,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { struct DropData<'tcx> { dropck_result: DropckOutlivesResult<'tcx>, - region_constraint_data: Option>>>, + region_constraint_data: Option>>, } struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { 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 9409fefb6bde7..cdbbe1d02bd92 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -4,7 +4,8 @@ use crate::borrow_check::borrow_set::BorrowSet; use crate::borrow_check::location::LocationTable; -use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::member_constraints::MemberConstraintSet; use crate::borrow_check::nll::facts::AllFacts; use crate::borrow_check::nll::region_infer::values::LivenessValues; use crate::borrow_check::nll::region_infer::values::PlaceholderIndex; @@ -23,7 +24,7 @@ use crate::dataflow::MaybeInitializedPlaces; use either::Either; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -127,7 +128,8 @@ pub(crate) fn type_check<'tcx>( placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::new(elements.clone()), - outlives_constraints: ConstraintSet::default(), + outlives_constraints: OutlivesConstraintSet::default(), + member_constraints: MemberConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), }; @@ -215,7 +217,7 @@ fn translate_outlives_facts(cx: &mut BorrowCheckContext<'_, '_>) { let location_table = cx.location_table; facts .outlives - .extend(cx.constraints.outlives_constraints.iter().flat_map( + .extend(cx.constraints.outlives_constraints.outlives().iter().flat_map( |constraint: &OutlivesConstraint| { if let Some(from_location) = constraint.locations.from_location() { Either::Left(iter::once(( @@ -582,7 +584,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { ); let locations = location.to_locations(); - for constraint in constraints.iter() { + for constraint in constraints.outlives().iter() { let mut constraint = *constraint; constraint.locations = locations; if let ConstraintCategory::Return @@ -834,6 +836,7 @@ struct TypeChecker<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, last_span: Span, + body: &'a Body<'tcx>, /// User type annotations are shared between the main MIR and the MIR of /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, @@ -884,7 +887,9 @@ crate struct MirTypeckRegionConstraints<'tcx> { /// hence it must report on their liveness constraints. crate liveness_constraints: LivenessValues, - crate outlives_constraints: ConstraintSet, + crate outlives_constraints: OutlivesConstraintSet, + + crate member_constraints: MemberConstraintSet<'tcx, RegionVid>, crate closure_bounds_mapping: FxHashMap>, @@ -992,6 +997,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { infcx, last_span: DUMMY_SP, mir_def_id, + body, user_type_annotations: &body.user_type_annotations, param_env, region_bound_pairs, @@ -1093,7 +1099,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &mut self, locations: Locations, category: ConstraintCategory, - data: &[QueryRegionConstraint<'tcx>], + data: &QueryRegionConstraints<'tcx>, ) { debug!( "push_region_constraints: constraints generated at {:?} are {:#?}", @@ -1109,7 +1115,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations, category, &mut self.borrowck_context.constraints, - ).convert_all(&data); + ).convert_all(data); } /// Convenient wrapper around `relate_tys::relate_types` -- see @@ -1229,6 +1235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let infcx = self.infcx; let tcx = infcx.tcx; let param_env = self.param_env; + let body = self.body; debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); let opaque_type_map = self.fully_perform_op( locations, @@ -1244,6 +1251,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { dummy_body_id, param_env, &anon_ty, + locations.span(body), )); debug!( "eq_opaque_type_and_type: \ @@ -2508,10 +2516,20 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location: Location, ) -> ty::InstantiatedPredicates<'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements { - let closure_constraints = - closure_region_requirements.apply_requirements(tcx, def_id, substs); + let closure_constraints = QueryRegionConstraints { + outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs), + + // Presently, closures never propagate member + // constraints to their parents -- they are enforced + // locally. This is largely a non-issue as member + // constraints only come from `-> impl Trait` and + // friends which don't appear (thus far...) in + // closures. + member_constraints: vec![], + }; let bounds_mapping = closure_constraints + .outlives .iter() .enumerate() .filter_map(|(idx, constraint)| { diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index bbb0825ee082a..5c23ad4a4edfb 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -17,6 +17,7 @@ use rustc::infer::canonical::{ CanonicalVarValues, OriginalQueryValues, QueryResponse, + QueryRegionConstraints, Certainty, }; use rustc::traits::{ @@ -151,14 +152,14 @@ impl context::AggregateOps> for ChalkContext<'tcx> { let solution = constrained_subst.unchecked_map(|cs| match ambiguous { true => QueryResponse { var_values: cs.subst.make_identity(self.tcx), - region_constraints: Vec::new(), + region_constraints: QueryRegionConstraints::default(), certainty: Certainty::Ambiguous, value: (), }, false => QueryResponse { var_values: cs.subst, - region_constraints: Vec::new(), + region_constraints: QueryRegionConstraints::default(), // FIXME: restore this later once we get better at handling regions // region_constraints: cs.constraints diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 37866bab9009d..b0be37772af39 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -856,7 +856,8 @@ fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::TypeckT let revealed_ty = if tcx.features().impl_trait_in_bindings { fcx.instantiate_opaque_types_from_value( id, - &expected_type + &expected_type, + body.value.span, ) } else { expected_type @@ -962,7 +963,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { self.fcx.instantiate_opaque_types_from_value( self.parent_id, - &o_ty + &o_ty, + ty.span, ) } else { o_ty @@ -1058,7 +1060,11 @@ fn check_fn<'a, 'tcx>( let declared_ret_ty = fn_sig.output(); fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - let revealed_ret_ty = fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty); + let revealed_ret_ty = fcx.instantiate_opaque_types_from_value( + fn_id, + &declared_ret_ty, + decl.output.span(), + ); fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); fn_sig = fcx.tcx.mk_fn_sig( fn_sig.inputs().iter().cloned(), @@ -2445,6 +2451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, parent_id: hir::HirId, value: &T, + value_span: Span, ) -> T { let parent_def_id = self.tcx.hir().local_def_id_from_hir_id(parent_id); debug!("instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", @@ -2457,6 +2464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.body_id, self.param_env, value, + value_span, ) ); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f97e9d43854fc..e6a09e7f873ea 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -570,6 +570,9 @@ declare_features! ( // Allows explicit discriminants on non-unit enum variants. (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), + // Allows `impl Trait` with multiple unrelated lifetimes. + (active, member_constraints, "1.37.0", Some(61977), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 266bd2a04a222..aee988d5148ae 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -389,6 +389,7 @@ symbols! { match_beginning_vert, match_default_bindings, may_dangle, + member_constraints, message, meta, min_const_fn, diff --git a/src/test/ui/async-await/async-fn-multiple-lifetimes.rs b/src/test/ui/async-await/async-fn-multiple-lifetimes.rs deleted file mode 100644 index e3ac817b15ca5..0000000000000 --- a/src/test/ui/async-await/async-fn-multiple-lifetimes.rs +++ /dev/null @@ -1,19 +0,0 @@ -// edition:2018 - -#![feature(arbitrary_self_types, async_await, await_macro, pin)] - -use std::ops::Add; - -async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} -//~^ ERROR ambiguous lifetime bound in `async fn` - -async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( - _: impl for<'a> Add<&'a u8>, - _: impl for<'b> Add<&'b u8>, - _: &'c u8, -) {} - -async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} -//~^ ambiguous lifetime bound in `async fn` - -fn main() {} diff --git a/src/test/ui/async-await/async-fn-multiple-lifetimes.stderr b/src/test/ui/async-await/async-fn-multiple-lifetimes.stderr deleted file mode 100644 index 8c3ee2bed830b..0000000000000 --- a/src/test/ui/async-await/async-fn-multiple-lifetimes.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: ambiguous lifetime bound in `async fn` - --> $DIR/async-fn-multiple-lifetimes.rs:7:65 - | -LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} - | ^ neither `'a` nor `'b` outlives the other - | - = note: multiple unrelated lifetimes are not allowed in `async fn`. - = note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime. - -error: ambiguous lifetime bound in `async fn` - --> $DIR/async-fn-multiple-lifetimes.rs:16:52 - | -LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} - | ^ the elided lifetimes here do not outlive one another - | - = note: multiple unrelated lifetimes are not allowed in `async fn`. - = note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime. - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/async-await/multiple-lifetimes/elided.rs b/src/test/ui/async-await/multiple-lifetimes/elided.rs new file mode 100644 index 0000000000000..45f3170d4c309 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/elided.rs @@ -0,0 +1,12 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(async_await)] + +async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} + +fn main() { + let _ = multiple_elided_lifetimes(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs b/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs new file mode 100644 index 0000000000000..a7254cee75526 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs @@ -0,0 +1,14 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(async_await)] + +async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8, _: fn(&u8)) {} + +fn gimme(_: &u8) { } + +fn main() { + let _ = multiple_named_lifetimes(&22, &44, gimme); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/hrtb.rs b/src/test/ui/async-await/multiple-lifetimes/hrtb.rs new file mode 100644 index 0000000000000..620b0080966b9 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/hrtb.rs @@ -0,0 +1,17 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(arbitrary_self_types, async_await, await_macro)] +#![allow(dead_code)] + +use std::ops::Add; + +async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( + _: impl for<'a> Add<&'a u8>, + _: impl for<'b> Add<&'b u8>, + _: &'c u8, +) {} + +fn main() {} diff --git a/src/test/ui/async-await/multiple-lifetimes/named.rs b/src/test/ui/async-await/multiple-lifetimes/named.rs new file mode 100644 index 0000000000000..7d13d48bc8bbd --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/named.rs @@ -0,0 +1,12 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(arbitrary_self_types, async_await, await_macro)] + +async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} + +fn main() { + let _ = multiple_named_lifetimes(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/partial-relation.rs b/src/test/ui/async-await/multiple-lifetimes/partial-relation.rs new file mode 100644 index 0000000000000..903c43950a5c4 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/partial-relation.rs @@ -0,0 +1,15 @@ +// edition:2018 +// run-pass + +#![feature(async_await)] + +async fn lotsa_lifetimes<'a, 'b, 'c>(a: &'a u32, b: &'b u32, c: &'c u32) -> (&'a u32, &'b u32) + where 'b: 'a +{ + drop((a, c)); + (b, b) +} + +fn main() { + let _ = lotsa_lifetimes(&22, &44, &66); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs new file mode 100644 index 0000000000000..08622311f7b1c --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs @@ -0,0 +1,18 @@ +// edition:2018 +// run-pass + +// Test that a feature gate is needed to use `impl Trait` as the +// return type of an async. + +#![feature(async_await, member_constraints)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + (a, b) +} + +fn main() { + let _ = async_ret_impl_trait(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs new file mode 100644 index 0000000000000..08ecea4cc85fc --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs @@ -0,0 +1,18 @@ +// edition:2018 + +// Test that a feature gate is needed to use `impl Trait` as the +// return type of an async. + +#![feature(async_await)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + //~^ ERROR ambiguous lifetime bound + (a, b) +} + +fn main() { + let _ = async_ret_impl_trait(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr new file mode 100644 index 0000000000000..de2c85d772a72 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr @@ -0,0 +1,10 @@ +error: ambiguous lifetime bound in `impl Trait` + --> $DIR/ret-impl-trait-no-fg.rs:11:64 + | +LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other + | + = help: add #![feature(member_constraints)] to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr new file mode 100644 index 0000000000000..b4d5d3ec051e2 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/ret-impl-trait-one.rs:12:80 + | +LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { + | ________________________________--__--__________________________________________^ + | | | | + | | | lifetime `'b` defined here + | | lifetime `'a` defined here +LL | | +LL | | (a, b) +LL | | } + | |_^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs new file mode 100644 index 0000000000000..e1b714652737f --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs @@ -0,0 +1,27 @@ +// edition:2018 + +// Test that a feature gate is needed to use `impl Trait` as the +// return type of an async. + +#![feature(async_await, member_constraints)] + +trait Trait<'a> { } +impl Trait<'_> for T { } + +// Only `'a` permitted in return type, not `'b`. +async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { + //~^ ERROR lifetime mismatch + (a, b) +} + +// As above, but `'b: 'a`, so return type can be inferred to `(&'a u8, +// &'a u8)`. +async fn async_ret_impl_trait2<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> +where + 'b: 'a, +{ + (a, b) +} + +fn main() { +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr new file mode 100644 index 0000000000000..f6d611517bc8f --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr @@ -0,0 +1,11 @@ +error[E0623]: lifetime mismatch + --> $DIR/ret-impl-trait-one.rs:12:65 + | +LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { + | ------ ^^^^^^^^^^^^^^ + | | | + | | ...but data from `b` is returned here + | this parameter and the return type are declared with different lifetimes... + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-ref.rs b/src/test/ui/async-await/multiple-lifetimes/ret-ref.rs new file mode 100644 index 0000000000000..98da90161e5fd --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-ref.rs @@ -0,0 +1,46 @@ +// edition:2018 + +// Test that we get the expected borrow check errors when an async +// function (which takes multiple lifetimes) only returns data from +// one of them. + +#![feature(async_await)] + +async fn multiple_named_lifetimes<'a, 'b>(a: &'a u8, _: &'b u8) -> &'a u8 { + a +} + +// Both are borrowed whilst the future is live. +async fn future_live() { + let mut a = 22; + let mut b = 44; + let future = multiple_named_lifetimes(&a, &b); + a += 1; //~ ERROR cannot assign + b += 1; //~ ERROR cannot assign + let p = future.await; + drop(p); +} + +// Just the return value is live after future is awaited. +async fn just_return_live() { + let mut a = 22; + let mut b = 44; + let future = multiple_named_lifetimes(&a, &b); + let p = future.await; + a += 1; //~ ERROR cannot assign + b += 1; + drop(p); +} + +// Once `p` is dead, both `a` and `b` are unborrowed. +async fn after_both_dead() { + let mut a = 22; + let mut b = 44; + let future = multiple_named_lifetimes(&a, &b); + let p = future.await; + drop(p); + a += 1; + b += 1; +} + +fn main() { } diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr new file mode 100644 index 0000000000000..fe70d35942c7c --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr @@ -0,0 +1,37 @@ +error[E0506]: cannot assign to `a` because it is borrowed + --> $DIR/ret-ref.rs:18:5 + | +LL | let future = multiple_named_lifetimes(&a, &b); + | -- borrow of `a` occurs here +LL | a += 1; + | ^^^^^^ assignment to borrowed `a` occurs here +LL | b += 1; +LL | let p = future.await; + | ------ borrow later used here + +error[E0506]: cannot assign to `b` because it is borrowed + --> $DIR/ret-ref.rs:19:5 + | +LL | let future = multiple_named_lifetimes(&a, &b); + | -- borrow of `b` occurs here +LL | a += 1; +LL | b += 1; + | ^^^^^^ assignment to borrowed `b` occurs here +LL | let p = future.await; + | ------ borrow later used here + +error[E0506]: cannot assign to `a` because it is borrowed + --> $DIR/ret-ref.rs:30:5 + | +LL | let future = multiple_named_lifetimes(&a, &b); + | -- borrow of `a` occurs here +LL | let p = future.await; +LL | a += 1; + | ^^^^^^ assignment to borrowed `a` occurs here +LL | b += 1; +LL | drop(p); + | - borrow later used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/async-await/multiple-lifetimes/variance.rs b/src/test/ui/async-await/multiple-lifetimes/variance.rs new file mode 100644 index 0000000000000..b52ad17d5631d --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/variance.rs @@ -0,0 +1,18 @@ +// edition:2018 +// run-pass + +// Test for async fn where the parameters have distinct lifetime +// parameters that appear in all possible variances. + +#![feature(async_await)] + +#[allow(dead_code)] +async fn lotsa_lifetimes<'a, 'b, 'c>(_: fn(&'a u8), _: fn(&'b u8) -> &'b u8, _: fn() -> &'c u8) { } + +fn take_any(_: &u8) { } +fn identify(x: &u8) -> &u8 { x } +fn give_back() -> &'static u8 { &22 } + +fn main() { + let _ = lotsa_lifetimes(take_any, identify, give_back); +} diff --git a/src/test/ui/feature-gates/feature-gate-member-constraints.rs b/src/test/ui/feature-gates/feature-gate-member-constraints.rs new file mode 100644 index 0000000000000..293a93352e641 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-member-constraints.rs @@ -0,0 +1,9 @@ +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T {} + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { + //~^ ERROR ambiguous lifetime bound + (x, y) +} + +fn main() { } diff --git a/src/test/ui/feature-gates/feature-gate-member-constraints.stderr b/src/test/ui/feature-gates/feature-gate-member-constraints.stderr new file mode 100644 index 0000000000000..3745d5e1c59d6 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-member-constraints.stderr @@ -0,0 +1,10 @@ +error: ambiguous lifetime bound in `impl Trait` + --> $DIR/feature-gate-member-constraints.rs:4:43 + | +LL | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other + | + = help: add #![feature(member_constraints)] to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs new file mode 100644 index 0000000000000..61e858ee02d44 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -0,0 +1,22 @@ +// compile-flags:-Zborrowck=mir + +#![feature(member_constraints)] +#![feature(existential_type)] + +#[derive(Clone)] +struct CopyIfEq(T, U); + +impl Copy for CopyIfEq {} + +existential type E<'a, 'b>: Sized; + +fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + //~^ ERROR lifetime may not live long enough + let v = CopyIfEq::<*mut _, *mut _>(&mut {x}, &mut y); + let u = v; + let _: *mut &'a i32 = u.1; + unsafe { let _: &'b i32 = *u.0; } + u.0 +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr new file mode 100644 index 0000000000000..b59dfbe9f2ada --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -0,0 +1,12 @@ +error: lifetime may not live long enough + --> $DIR/error-handling.rs:13:56 + | +LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | -- lifetime `'a` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'static` +help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a constraint + | +LL | existential type E<'a, 'b>: Sized; + 'a + | + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs new file mode 100644 index 0000000000000..2da3886bb552b --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -0,0 +1,54 @@ +// edition:2018 +// run-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(member_constraints)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Invert<'a> <: Invert<'b>` if `'b: 'a`, unlike most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Invert<'a>(fn(&'a u8)); + +fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> +where + 'c: 'a, + 'c: 'b, + 'd: 'c, +{ + // Representing the where clauses as a graph, where `A: B` is an + // edge `B -> A`: + // + // ``` + // 'a -> 'c -> 'd + // ^ + // | + // 'b + // ``` + // + // Meanwhile we return a value &'0 u8 where we have the constraints: + // + // ``` + // '0: 'a + // '0: 'b + // '0 in ['d, 'e] + // ``` + // + // Here, ignoring the "in" constraint, the minimal choice for `'0` + // is `'c`, but that is not in the "in set". Still, that reduces + // the range of options in the "in set" to just `'d` (`'e: 'c` + // does not hold). + let p = if condition() { a } else { b }; + p +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr new file mode 100644 index 0000000000000..4de872e8441ef --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr @@ -0,0 +1,19 @@ +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/inverse-bounds.rs:16:70 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `Invert<'_>` captures lifetime '_#8r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` + +warning: the feature `pin` has been stable since 1.33.0 and no longer requires an attribute to enable + --> $DIR/inverse-bounds.rs:4:60 + | +LL | #![feature(arbitrary_self_types, async_await, await_macro, pin)] + | ^^^ + | + = note: #[warn(stable_features)] on by default + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs new file mode 100644 index 0000000000000..5f484773405cd --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs @@ -0,0 +1,29 @@ +// edition:2018 +// compile-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(member_constraints)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +// Test case where we have elision in the impl trait and we have to +// pick the right region. + +// Ultimately `Trait<'x, 'static>`. +fn upper_bounds1(a: &u8) -> impl Trait<'_, 'static> { + (a, a) +} + +// Ultimately `Trait<'x, 'x>`, so not really multiple bounds. +fn upper_bounds2(a: &u8) -> impl Trait<'_, '_> { + (a, a) +} + +// Kind of a weird annoying case. +fn upper_bounds3<'b>(a: &u8) -> impl Trait<'_, 'b> { + (a, a) +} + +fn main() { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs new file mode 100644 index 0000000000000..c17ae6f0519bf --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -0,0 +1,32 @@ +// edition:2018 +// compile-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(member_constraints)] +#![feature(existential_type)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +// Here we wind up selecting `'a` and `'b` in the hidden type because +// those are the types that appear in the original values. + +existential type Foo<'a, 'b>: Trait<'a, 'b>; + +fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'a, 'b> { + // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['a, 'b] + // '1 in ['a, 'b] + // ``` + // + // We use the fact that `'a: 0'` must hold (combined with the in + // constraint) to determine that `'0 = 'a` must be the answer. + (a, b) +} + +fn main() { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs new file mode 100644 index 0000000000000..31891ef15c754 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -0,0 +1,29 @@ +// edition:2018 +// compile-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(member_constraints)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +// Here we wind up selecting `'a` and `'b` in the hidden type because +// those are the types that appear in the original values. + +fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['a, 'b] + // '1 in ['a, 'b] + // ``` + // + // We use the fact that `'a: 0'` must hold (combined with the in + // constraint) to determine that `'0 = 'a` must be the answer. + (a, b) +} + +fn main() { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs new file mode 100644 index 0000000000000..29c997085d8f4 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs @@ -0,0 +1,46 @@ +// edition:2018 +// compile-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(member_constraints)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Ordinary<'a>(&'a u8); + +// Here we wind up selecting `'e` in the hidden type because +// we need something outlived by both `'a` and `'b` and only `'e` applies. + +fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> +where + 'a: 'e, + 'b: 'e, + 'a: 'd, +{ + // We return a value: + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['d, 'e] + // ``` + // + // but we don't have it. + // + // We are forced to pick that '0 = 'e, because only 'e is outlived by *both* 'a and 'b. + let p = if condition() { a } else { b }; + p +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr new file mode 100644 index 0000000000000..a255c48ec6ef1 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr @@ -0,0 +1,9 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unrelated.rs:18:74 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs new file mode 100644 index 0000000000000..db1641b0140b9 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs @@ -0,0 +1,38 @@ +// edition:2018 + +#![feature(member_constraints)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Ordinary<'a>(&'a u8); + +// Here we get an error because none of our choices (either `'d` nor `'e`) are outlived +// by both `'a` and `'b`. + +fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> +//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds +where + 'a: 'e, + 'b: 'd, +{ + // Hidden type `Ordinary<'0>` with constraints: + // + // ``` + // 'a: '0 + // 'b: '0 + // 'a in ['d, 'e] + // ``` + if condition() { a } else { b } +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr new file mode 100644 index 0000000000000..cd2d46ac18218 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -0,0 +1,21 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unrelated.rs:18:74 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + | +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 23:1 + --> $DIR/ordinary-bounds-unrelated.rs:23:1 + | +LL | / { +LL | | // Hidden type `Ordinary<'0>` with constraints: +LL | | // +LL | | // ``` +... | +LL | | if condition() { a } else { b } +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr new file mode 100644 index 0000000000000..af42ed1c5c15a --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr @@ -0,0 +1,9 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unsuited.rs:20:62 + | +LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs new file mode 100644 index 0000000000000..7f9c92f15a2f9 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs @@ -0,0 +1,41 @@ +// edition:2018 + +#![feature(member_constraints)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Ordinary<'a>(&'a u8); + +// Here we need something outlived by `'a` *and* outlived by `'b`, but +// we can only name `'a` and `'b` (and neither suits). So we get an +// error. Somewhat unfortunate, though, since the caller would have to +// consider the loans for both `'a` and `'b` alive. + +fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + //~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds +{ + // We return a value: + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['a, 'b] + // ``` + // + // but we don't have it. + // + // We are forced to pick that '0 = 'e, because only 'e is outlived by *both* 'a and 'b. + if condition() { a } else { b } +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr new file mode 100644 index 0000000000000..59ce93fa78b6b --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -0,0 +1,21 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unsuited.rs:20:62 + | +LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + | ^^^^^^^^^^^^^^^^^^ + | +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 22:1 + --> $DIR/ordinary-bounds-unsuited.rs:22:1 + | +LL | / { +LL | | // We return a value: +LL | | // +LL | | // ``` +... | +LL | | if condition() { a } else { b } +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.rs b/src/test/ui/impl-trait/needs_least_region_or_bound.rs index 2a5b365559e77..52475f65a8353 100644 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/needs_least_region_or_bound.rs @@ -1,10 +1,24 @@ +// run-pass + +#![feature(member_constraints)] + use std::fmt::Debug; trait MultiRegionTrait<'a, 'b> {} impl<'a, 'b> MultiRegionTrait<'a, 'b> for (&'a u32, &'b u32) {} fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { -//~^ ERROR ambiguous lifetime bound + // Here we have a constraint that: + // + // (x, y) has type (&'0 u32, &'1 u32) + // + // where + // + // 'a: '0 + // + // then we require that `('0 u32, &'1 u32): MultiRegionTrait<'a, + // 'b>`, which winds up imposing a requirement that `'0 = 'a` and + // `'1 = 'b`. (x, y) } diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.stderr b/src/test/ui/impl-trait/needs_least_region_or_bound.stderr deleted file mode 100644 index f1b4d9c58f39f..0000000000000 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: ambiguous lifetime bound in `impl Trait` - --> $DIR/needs_least_region_or_bound.rs:6:55 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other - -error: aborting due to previous error -