diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 6af0cee948d..844250f51a0 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -63,9 +63,12 @@ impl<'a> HashStable> for ty::RegionKind { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - ty::ReErased | ty::ReStatic | ty::ReEmpty => { + ty::ReErased | ty::ReStatic => { // No variant fields to hash for these ... } + ty::ReEmpty(universe) => { + universe.hash_stable(hcx, hasher); + } ty::ReLateBound(db, ty::BrAnon(i)) => { db.hash_stable(hcx, hasher); i.hash_stable(hcx, hasher); diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index b720168f356..48a6c6d7413 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -167,11 +167,17 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match r { - ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r, + ty::ReFree(_) + | ty::ReErased + | ty::ReStatic + | ty::ReEmpty(ty::UniverseIndex::ROOT) + | ty::ReEarlyBound(..) => r, + ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) }, r, ), + ty::ReVar(vid) => { let universe = canonicalizer.region_var_universe(*vid); canonicalizer.canonical_var_for_region( @@ -179,6 +185,11 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { r, ) } + + ty::ReEmpty(ui) => { + bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME + } + _ => { // Other than `'static` or `'empty`, the query // response should be executing in a fully @@ -213,7 +224,7 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match r { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r, + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r, ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), _ => { // We only expect region names that the user can type. @@ -320,8 +331,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::ReEarlyBound(..) | ty::ReFree(_) | ty::ReScope(_) + | ty::ReEmpty(_) | ty::RePlaceholder(..) - | ty::ReEmpty | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r), ty::ReClosureBound(..) => { diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 5d765a2a3d3..2518805a1ec 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -577,7 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { ty::RePlaceholder(..) | ty::ReVar(..) - | ty::ReEmpty + | ty::ReEmpty(_) | ty::ReStatic | ty::ReScope(..) | ty::ReEarlyBound(..) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 58566bdcc35..57a52a991ed 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -138,7 +138,10 @@ pub(super) fn note_and_explain_region( msg_span_from_free_region(tcx, region) } - ty::ReEmpty => ("the empty lifetime".to_owned(), None), + ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), None), + + // uh oh, hope no user ever sees THIS + ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None), ty::RePlaceholder(_) => (format!("any other region"), None), @@ -181,7 +184,8 @@ fn msg_span_from_free_region( msg_span_from_early_bound_and_free_regions(tcx, region) } ty::ReStatic => ("the static lifetime".to_owned(), None), - ty::ReEmpty => ("an empty lifetime".to_owned(), None), + ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), None), + ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), None), _ => bug!("{:?}", region), } } @@ -375,6 +379,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + RegionResolutionError::UpperBoundUniverseConflict( + _, + _, + var_universe, + sup_origin, + sup_r, + ) => { + assert!(sup_r.is_placeholder()); + + // Make a dummy value for the "sub region" -- + // this is the initial value of the + // placeholder. In practice, we expect more + // tailored errors that don't really use this + // value. + let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe)); + + self.report_placeholder_failure( + region_scope_tree, + sup_origin, + sub_r, + sup_r, + ) + .emit(); + } + RegionResolutionError::MemberConstraintFailure { opaque_type_def_id, hidden_ty, @@ -429,6 +458,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::GenericBoundFailure(..) => true, RegionResolutionError::ConcreteFailure(..) | RegionResolutionError::SubSupConflict(..) + | RegionResolutionError::UpperBoundUniverseConflict(..) | RegionResolutionError::MemberConstraintFailure { .. } => false, }; @@ -443,6 +473,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::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), RegionResolutionError::MemberConstraintFailure { span, .. } => span, }); errors diff --git a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs index 8f4c6439920..6a9fe19e1ac 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -45,7 +45,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// /// It will later be extended to trait objects. pub(super) fn try_report_anon_anon_conflict(&self) -> Option { - let (span, sub, sup) = self.regions(); + let (span, sub, sup) = self.regions()?; // Determine whether the sub and sup consist of both anonymous (elided) regions. let anon_reg_sup = self.tcx().is_suitable_region(sup)?; diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs index 8749d6cd34b..b10a60ef6f1 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs @@ -17,11 +17,6 @@ mod util; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { - match *error { - ConcreteFailure(..) | SubSupConflict(..) => {} - _ => return false, // inapplicable - } - if let Some(tables) = self.in_progress_tables { let tables = tables.borrow(); NiceRegionError::new(self, error.clone(), Some(&tables)).try_report().is_some() @@ -79,13 +74,14 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { .or_else(|| self.try_report_impl_not_conforming_to_trait()) } - pub fn regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) { + pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { match (&self.error, self.regions) { - (Some(ConcreteFailure(origin, sub, sup)), None) => (origin.span(), sub, sup), - (Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => (origin.span(), sub, sup), - (None, Some((span, sub, sup))) => (span, sub, sup), - (Some(_), Some(_)) => panic!("incorrectly built NiceRegionError"), - _ => panic!("trying to report on an incorrect lifetime failure"), + (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)), + (Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => { + Some((origin.span(), sub, sup)) + } + (None, Some((span, sub, sup))) => Some((span, sub, sup)), + _ => None, } } } diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs index df37f53606b..250dcff372c 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -9,7 +9,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// When given a `ConcreteFailure` for a function with parameters containing a named region and /// an anonymous region, emit an descriptive diagnostic error. pub(super) fn try_report_named_anon_conflict(&self) -> Option> { - let (span, sub, sup) = self.regions(); + let (span, sub, sup) = self.regions()?; debug!( "try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})", diff --git a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs index 7b31fe7cd7e..0b0bd61ce77 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -107,6 +107,25 @@ impl NiceRegionError<'me, 'tcx> { found.substs, )), + Some(RegionResolutionError::UpperBoundUniverseConflict( + vid, + _, + _, + SubregionOrigin::Subtype(box TypeTrace { + cause, + values: ValuePairs::TraitRefs(ExpectedFound { expected, found }), + }), + sup_placeholder @ ty::RePlaceholder(_), + )) if expected.def_id == found.def_id => Some(self.try_report_placeholders_trait( + Some(self.tcx().mk_region(ty::ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + expected.def_id, + expected.substs, + found.substs, + )), + Some(RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 16087959972..cf61cac0ac4 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::ReScope(_) | ty::ReVar(_) | ty::RePlaceholder(..) - | ty::ReEmpty + | ty::ReEmpty(_) | ty::ReErased => { // replace all free regions with 'erased self.tcx().lifetimes.re_erased diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index bbca4823431..d25d186f4d7 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -128,6 +128,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { placeholder_map: &PlaceholderMap<'tcx>, snapshot: &CombinedSnapshot<'_, 'tcx>, ) -> RelateResult<'tcx, ()> { + // If the user gave `-Zno-leak-check`, or we have been + // configured to skip the leak check, then skip the leak check + // completely. The leak check is deprecated. Any legitimate + // subtyping errors that it would have caught will now be + // caught later on, during region checking. However, we + // continue to use it for a transition period. + if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() { + return Ok(()); + } + self.borrow_region_constraints().leak_check( self.tcx, overly_polymorphic, diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 4b1f8a5be14..e0a8c3b4e65 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -82,6 +82,16 @@ pub enum RegionResolutionError<'tcx> { Region<'tcx>, ), + /// Indicates a `'b: 'a` constraint where `'a` is in a universe that + /// cannot name the placeholder `'b`. + UpperBoundUniverseConflict( + RegionVid, + RegionVariableOrigin, + ty::UniverseIndex, // the universe index of the region variable + SubregionOrigin<'tcx>, // cause of the constraint + Region<'tcx>, // the placeholder `'b` + ), + /// 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. @@ -149,7 +159,14 @@ 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_fn_n( + |vid| { + let vid_universe = self.var_infos[vid].universe; + let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe)); + VarValue::Value(re_empty) + }, + self.num_vars(), + ), } } @@ -381,8 +398,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // This is a specialized version of the `lub_concrete_regions` // check below for a common case, here purely as an // optimization. - if let ReEmpty = a_region { - return false; + let b_universe = self.var_infos[b_vid].universe; + if let ReEmpty(a_universe) = a_region { + if *a_universe == b_universe { + return false; + } } let mut lub = self.lub_concrete_regions(a_region, cur_region); @@ -399,7 +419,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // tighter bound than `'static`. // // (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.) - let b_universe = self.var_infos[b_vid].universe; if let ty::RePlaceholder(p) = lub { if b_universe.cannot_name(p.universe) { lub = self.tcx().lifetimes.re_static; @@ -420,12 +439,38 @@ 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 { + let tcx = self.tcx(); + let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2); + + // Check for the case where we know that `'b: 'static` -- in that case, + // `a <= b` for all `a`. + let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b); + if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) { + return true; + } + + // If both `a` and `b` are free, consult the declared + // relationships. Note that this can be more precise than the + // `lub` relationship defined below, since sometimes the "lub" + // is actually the `postdom_upper_bound` (see + // `TransitiveRelation` for more details). + let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a); + if a_free_or_static && b_free_or_static { + return sub_free_regions(a, b); + } + + // For other cases, leverage the LUB code to find the LUB and + // check if it is equal to `b`. self.lub_concrete_regions(a, b) == b } - /// Returns the smallest region `c` such that `a <= c` and `b <= c`. + /// Returns the least-upper-bound of `a` and `b`; i.e., the + /// smallest region `c` such that `a <= c` and `b <= c`. + /// + /// Neither `a` nor `b` may be an inference variable (hence the + /// term "concrete regions"). fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - match (a, b) { + let r = match (a, b) { (&ty::ReClosureBound(..), _) | (_, &ty::ReClosureBound(..)) | (&ReLateBound(..), _) @@ -435,14 +480,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - (&ReEmpty, r) | (r, &ReEmpty) => { - r // everything lives longer than empty - } - (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { span_bug!( self.var_infos[v_id].origin.span(), @@ -453,6 +490,41 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } + (&ReStatic, _) | (_, &ReStatic) => { + // nothing lives longer than `'static` + self.tcx().lifetimes.re_static + } + + (&ReEmpty(_), r @ ReEarlyBound(_)) + | (r @ ReEarlyBound(_), &ReEmpty(_)) + | (&ReEmpty(_), r @ ReFree(_)) + | (r @ ReFree(_), &ReEmpty(_)) + | (&ReEmpty(_), r @ ReScope(_)) + | (r @ ReScope(_), &ReEmpty(_)) => { + // All empty regions are less than early-bound, free, + // and scope regions. + r + } + + (&ReEmpty(a_ui), &ReEmpty(b_ui)) => { + // Empty regions are ordered according to the universe + // they are associated with. + let ui = a_ui.min(b_ui); + self.tcx().mk_region(ReEmpty(ui)) + } + + (&ReEmpty(empty_ui), &RePlaceholder(placeholder)) + | (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => { + // If this empty region is from a universe that can + // name the placeholder, then the placeholder is + // larger; otherwise, the only ancestor is `'static`. + if empty_ui.can_name(placeholder.universe) { + self.tcx().mk_region(RePlaceholder(placeholder)) + } else { + self.tcx().lifetimes.re_static + } + } + (&ReEarlyBound(_), &ReScope(s_id)) | (&ReScope(s_id), &ReEarlyBound(_)) | (&ReFree(_), &ReScope(s_id)) @@ -509,7 +581,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().lifetimes.re_static } } - } + }; + + debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r); + + r } /// After expansion is complete, go and check upper bounds (i.e., @@ -528,7 +604,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } Constraint::RegSubReg(sub, sup) => { - if self.region_rels.is_subregion_of(sub, sup) { + if self.sub_concrete_regions(sub, sup) { continue; } @@ -557,7 +633,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // Do not report these errors immediately: // instead, set the variable value to error and // collect them later. - if !self.region_rels.is_subregion_of(a_region, b_region) { + if !self.sub_concrete_regions(a_region, b_region) { debug!( "collect_errors: region error at {:?}: \ cannot verify that {:?}={:?} <= {:?}", @@ -592,12 +668,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(self.tcx(), verify.region); - // This was an inference variable which didn't get - // constrained, therefore it can be assume to hold. - if let ty::ReEmpty = *sub { - continue; - } - let verify_kind_ty = verify.kind.to_ty(self.tcx()); if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) { continue; @@ -760,7 +830,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.sub_concrete_regions(effective_lower_bound, upper_bound.region) { let origin = self.var_infos[node_idx].origin; debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ @@ -780,6 +850,26 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + // If we have a scenario like `exists<'a> { forall<'b> { 'b: + // 'a } }`, we wind up without any lower-bound -- all we have + // are placeholders as upper bounds, but the universe of the + // variable `'a` doesn't permit those placeholders. + for upper_bound in &upper_bounds { + if let ty::RePlaceholder(p) = upper_bound.region { + if node_universe.cannot_name(p.universe) { + let origin = self.var_infos[node_idx].origin.clone(); + errors.push(RegionResolutionError::UpperBoundUniverseConflict( + node_idx, + origin, + node_universe, + upper_bound.origin.clone(), + upper_bound.region, + )); + return; + } + } + } + // Errors in earlier passes can yield error variables without // resolution errors here; delay ICE in favor of those errors. self.tcx().sess.delay_span_bug( @@ -890,7 +980,15 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } VerifyBound::OutlivedBy(r) => { - self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r)) + self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r)) + } + + VerifyBound::IsEmpty => { + if let ty::ReEmpty(_) = min { + true + } else { + false + } } VerifyBound::AnyBound(bs) => { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 4681a47317c..b93f4408cdc 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -125,6 +125,13 @@ pub struct InferCtxt<'a, 'tcx> { /// order, represented by its upper and lower bounds. pub type_variables: RefCell>, + /// If set, this flag causes us to skip the 'leak check' during + /// higher-ranked subtyping operations. This flag is a temporary one used + /// to manage the removal of the leak-check: for the time being, we still run the + /// leak-check, but we issue warnings. This flag can only be set to true + /// when entering a snapshot. + skip_leak_check: Cell, + /// Map from const parameter variable to the kind of const it represents. const_unification_table: RefCell>>>, @@ -246,7 +253,7 @@ pub enum ValuePairs<'tcx> { /// encounter an error or subtyping constraint. /// /// See the `error_reporting` module for more details. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct TypeTrace<'tcx> { cause: ObligationCause<'tcx>, values: ValuePairs<'tcx>, @@ -550,6 +557,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count(), in_snapshot: Cell::new(false), + skip_leak_check: Cell::new(false), region_obligations: RefCell::new(vec![]), universe: Cell::new(ty::UniverseIndex::ROOT), }) @@ -593,6 +601,7 @@ pub struct CombinedSnapshot<'a, 'tcx> { region_obligations_snapshot: usize, universe: ty::UniverseIndex, was_in_snapshot: bool, + was_skip_leak_check: bool, _in_progress_tables: Option>>, } @@ -720,6 +729,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { region_obligations_snapshot: self.region_obligations.borrow().len(), universe: self.universe(), was_in_snapshot: in_snapshot, + was_skip_leak_check: self.skip_leak_check.get(), // Borrow tables "in progress" (i.e., during typeck) // to ban writes from within a snapshot to them. _in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()), @@ -738,11 +748,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { region_obligations_snapshot, universe, was_in_snapshot, + was_skip_leak_check, _in_progress_tables, } = snapshot; self.in_snapshot.set(was_in_snapshot); self.universe.set(universe); + self.skip_leak_check.set(was_skip_leak_check); self.projection_cache.borrow_mut().rollback_to(projection_cache_snapshot); self.type_variables.borrow_mut().rollback_to(type_snapshot); @@ -765,10 +777,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { region_obligations_snapshot: _, universe: _, was_in_snapshot, + was_skip_leak_check, _in_progress_tables, } = snapshot; self.in_snapshot.set(was_in_snapshot); + self.skip_leak_check.set(was_skip_leak_check); self.projection_cache.borrow_mut().commit(projection_cache_snapshot); self.type_variables.borrow_mut().commit(type_snapshot); @@ -822,6 +836,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { r } + /// If `should_skip` is true, then execute `f` then unroll any bindings it creates. + pub fn probe_maybe_skip_leak_check(&self, should_skip: bool, f: F) -> R + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + { + debug!("probe()"); + let snapshot = self.start_snapshot(); + let skip_leak_check = should_skip || self.skip_leak_check.get(); + self.skip_leak_check.set(skip_leak_check); + let r = f(&snapshot); + self.rollback_to("probe", snapshot); + r + } + /// Scan the constraints produced since `snapshot` began and returns: /// /// - `None` -- if none of them involve "region outlives" constraints @@ -1647,12 +1675,6 @@ impl<'tcx> TypeTrace<'tcx> { } } -impl<'tcx> fmt::Debug for TypeTrace<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TypeTrace({:?})", self.cause) - } -} - impl<'tcx> SubregionOrigin<'tcx> { pub fn span(&self) -> Span { match *self { diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index d28507f6eb2..7fef9d27c4d 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -384,9 +384,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match least_region { None => least_region = Some(subst_arg), Some(lr) => { - if free_region_relations.sub_free_regions(lr, subst_arg) { + if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) { // keep the current least region - } else if free_region_relations.sub_free_regions(subst_arg, lr) { + } else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) { // switch to `subst_arg` least_region = Some(subst_arg); } else { @@ -611,7 +611,7 @@ pub fn unexpected_hidden_region_diagnostic( ); // Explain the region we are capturing. - if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region { + 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 @@ -844,7 +844,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { .emit(); } } - self.tcx.lifetimes.re_empty + self.tcx.lifetimes.re_root_empty } None => { self.tcx diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 8ee8482e79d..a2c99064caa 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -60,7 +60,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // scope type parameters: let param_bounds = param_bounds.chain(self.implicit_region_bound); - VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect()) + let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect(); + + if any_bounds.is_empty() { + // We know that all types `T` outlive `'empty`, so if we + // can find no other bound, then check that the region + // being tested is `'empty`. + VerifyBound::IsEmpty + } else { + // If we can find any other bound `R` such that `T: R`, then + // we don't need to check for `'empty`, because `R: 'empty`. + VerifyBound::AnyBound(any_bounds) + } } /// Given a projection like `T::Item`, searches the environment diff --git a/src/librustc/infer/region_constraints/leak_check.rs b/src/librustc/infer/region_constraints/leak_check.rs index 3b3a464ba55..29290cef2d2 100644 --- a/src/librustc/infer/region_constraints/leak_check.rs +++ b/src/librustc/infer/region_constraints/leak_check.rs @@ -33,18 +33,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> { assert!(self.in_snapshot()); - // If the user gave `-Zno-leak-check`, then skip the leak - // check completely. This is wildly unsound and also not - // unlikely to cause an ICE or two. It is intended for use - // only during a transition period, in which the MIR typeck - // uses the "universe-style" check, and the rest of typeck - // uses the more conservative leak check. Since the leak - // check is more conservative, we can't test the - // universe-style check without disabling it. - if tcx.sess.opts.debugging_opts.no_leak_check { - return Ok(()); - } - // Go through each placeholder that we created. for (_, &placeholder_region) in placeholder_map { // Find the universe this placeholder inhabits. diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 410058b70b5..2c580e2e349 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -233,6 +233,9 @@ pub enum VerifyBound<'tcx> { /// if `R: min`, then by transitivity `G: min`. OutlivedBy(Region<'tcx>), + /// Given a region `R`, true if it is `'empty`. + IsEmpty, + /// Given a set of bounds `B`, expands to the function: /// /// ```rust @@ -792,10 +795,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { match *region { ty::ReScope(..) | ty::ReStatic - | ty::ReEmpty | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => ty::UniverseIndex::ROOT, + ty::ReEmpty(ui) => ui, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReClosureBound(vid) | ty::ReVar(vid) => self.var_universe(vid), ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), @@ -867,6 +870,7 @@ impl<'tcx> VerifyBound<'tcx> { VerifyBound::IfEq(..) => false, VerifyBound::OutlivedBy(ty::ReStatic) => true, VerifyBound::OutlivedBy(_) => false, + VerifyBound::IsEmpty => false, VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), } @@ -875,7 +879,7 @@ impl<'tcx> VerifyBound<'tcx> { pub fn cannot_hold(&self) -> bool { match self { VerifyBound::IfEq(_, b) => b.cannot_hold(), - VerifyBound::OutlivedBy(ty::ReEmpty) => true, + VerifyBound::IsEmpty => false, VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 355f949b870..62ccd946744 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -4,8 +4,8 @@ //! and use that to decide when one free region outlives another, and so forth. use crate::middle::region; -use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations}; -use crate::ty::{self, Region, TyCtxt}; +use crate::ty::free_region_map::FreeRegionMap; +use crate::ty::{Region, TyCtxt}; use rustc_hir::def_id::DefId; /// Combines a `region::ScopeTree` (which governs relationships between @@ -38,62 +38,6 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> { Self { tcx, context, region_scope_tree, free_regions } } - /// Determines whether one region is a subregion of another. This is intended to run *after - /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs. - pub fn is_subregion_of( - &self, - sub_region: ty::Region<'tcx>, - super_region: ty::Region<'tcx>, - ) -> bool { - let result = sub_region == super_region || { - match (sub_region, super_region) { - (ty::ReEmpty, _) | (_, ty::ReStatic) => true, - - (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => { - self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope) - } - - (ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => { - let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br); - self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope) - } - - (ty::ReScope(sub_scope), ty::ReFree(fr)) => { - let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr); - self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope) - } - - (ty::ReEarlyBound(_), ty::ReEarlyBound(_)) - | (ty::ReFree(_), ty::ReEarlyBound(_)) - | (ty::ReEarlyBound(_), ty::ReFree(_)) - | (ty::ReFree(_), ty::ReFree(_)) => { - self.free_regions.sub_free_regions(sub_region, super_region) - } - - _ => false, - } - }; - let result = result || self.is_static(super_region); - debug!( - "is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}", - sub_region, super_region, result - ); - result - } - - /// Determines whether this free region is required to be `'static`. - fn is_static(&self, super_region: ty::Region<'tcx>) -> bool { - debug!("is_static(super_region={:?})", super_region); - match *super_region { - ty::ReStatic => true, - ty::ReEarlyBound(_) | ty::ReFree(_) => { - let re_static = self.tcx.mk_region(ty::ReStatic); - self.free_regions.sub_free_regions(&re_static, &super_region) - } - _ => false, - } - } - pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { self.free_regions.lub_free_regions(self.tcx, r_a, r_b) } diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index 29ea47809a0..855da0367de 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -7,6 +7,7 @@ use crate::infer::{CombinedSnapshot, InferOk}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::IntercrateMode; +use crate::traits::SkipLeakCheck; use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext}; use crate::ty::fold::TypeFoldable; use crate::ty::subst::Subst; @@ -53,6 +54,7 @@ pub fn overlapping_impls( impl1_def_id: DefId, impl2_def_id: DefId, intercrate_mode: IntercrateMode, + skip_leak_check: SkipLeakCheck, on_overlap: F1, no_overlap: F2, ) -> R @@ -70,7 +72,7 @@ where let overlaps = tcx.infer_ctxt().enter(|infcx| { let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode); - overlap(selcx, impl1_def_id, impl2_def_id).is_some() + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some() }); if !overlaps { @@ -83,7 +85,7 @@ where tcx.infer_ctxt().enter(|infcx| { let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode); selcx.enable_tracking_intercrate_ambiguity_causes(); - on_overlap(overlap(selcx, impl1_def_id, impl2_def_id).unwrap()) + on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap()) }) } @@ -113,12 +115,15 @@ fn with_fresh_ty_vars<'cx, 'tcx>( /// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls. fn overlap<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, + skip_leak_check: SkipLeakCheck, a_def_id: DefId, b_def_id: DefId, ) -> Option> { debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id); - selcx.infcx().probe(|snapshot| overlap_within_probe(selcx, a_def_id, b_def_id, snapshot)) + selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { + overlap_within_probe(selcx, a_def_id, b_def_id, snapshot) + }) } fn overlap_within_probe( @@ -146,7 +151,9 @@ fn overlap_within_probe( .eq_impl_headers(&a_impl_header, &b_impl_header) { Ok(InferOk { obligations, value: () }) => obligations, - Err(_) => return None, + Err(_) => { + return None; + } }; debug!("overlap: unification check succeeded"); diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index e88f4e65c7e..50068b89687 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -83,6 +83,28 @@ pub enum IntercrateMode { Fixed, } +/// Whether to skip the leak check, as part of a future compatibility warning step. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum SkipLeakCheck { + Yes, + No, +} + +impl SkipLeakCheck { + fn is_yes(self) -> bool { + self == SkipLeakCheck::Yes + } +} + +/// The "default" for skip-leak-check corresponds to the current +/// behavior (do not skip the leak check) -- not the behavior we are +/// transitioning into. +impl Default for SkipLeakCheck { + fn default() -> Self { + SkipLeakCheck::No + } +} + /// The mode that trait queries run in. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum TraitQueryMode { diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index e559ea391cd..8b68d6f2603 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -19,6 +19,7 @@ use crate::ty::{self, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir::def_id::DefId; +use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::DUMMY_SP; @@ -97,7 +98,7 @@ pub fn translate_substs<'a, 'tcx>( |_| { bug!( "When translating substitutions for specialization, the expected \ - specialization failed to hold" + specialization failed to hold" ) }, ) @@ -268,7 +269,7 @@ fn fulfill_implication<'a, 'tcx>( // no dice! debug!( "fulfill_implication: for impls on {:?} and {:?}, \ - could not fulfill: {:?} given {:?}", + could not fulfill: {:?} given {:?}", source_trait_ref, target_trait_ref, errors, param_env.caller_bounds ); Err(()) @@ -342,6 +343,7 @@ pub(super) fn specialization_graph_provider( FutureCompatOverlapErrorKind::Issue33140 => { ORDER_DEPENDENT_TRAIT_OBJECTS } + FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, }; tcx.struct_span_lint_hir( lint, diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index c90fa428001..98908e672f0 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -11,6 +11,7 @@ pub use rustc::traits::types::specialization_graph::*; pub enum FutureCompatOverlapErrorKind { Issue43355, Issue33140, + LeakCheck, } #[derive(Debug)] @@ -111,6 +112,7 @@ impl<'tcx> Children { possible_sibling, impl_def_id, traits::IntercrateMode::Issue43355, + traits::SkipLeakCheck::default(), |overlap| { if let Some(overlap_kind) = tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) @@ -161,6 +163,7 @@ impl<'tcx> Children { possible_sibling, impl_def_id, traits::IntercrateMode::Fixed, + traits::SkipLeakCheck::default(), |overlap| { last_lint = Some(FutureCompatOverlapError { error: overlap_error(overlap), @@ -169,6 +172,23 @@ impl<'tcx> Children { }, || (), ); + + if last_lint.is_none() { + traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::IntercrateMode::Fixed, + traits::SkipLeakCheck::Yes, + |overlap| { + last_lint = Some(FutureCompatOverlapError { + error: overlap_error(overlap), + kind: FutureCompatOverlapErrorKind::LeakCheck, + }); + }, + || (), + ); + } } // no overlap (error bailed already via ?) @@ -247,7 +267,7 @@ impl<'tcx> Graph { if trait_ref.references_error() { debug!( "insert: inserting dummy node for erroneous TraitRef {:?}, \ - impl_def_id={:?}, trait_def_id={:?}", + impl_def_id={:?}, trait_def_id={:?}", trait_ref, impl_def_id, trait_def_id ); @@ -326,7 +346,7 @@ impl<'tcx> Graph { if self.parent.insert(child, parent).is_some() { bug!( "When recording an impl from the crate store, information about its parent \ - was already present." + was already present." ); } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 8386058f72a..92c5600362e 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -173,8 +173,13 @@ pub struct CommonTypes<'tcx> { } pub struct CommonLifetimes<'tcx> { - pub re_empty: Region<'tcx>, + /// `ReEmpty` in the root universe. + pub re_root_empty: Region<'tcx>, + + /// `ReStatic` pub re_static: Region<'tcx>, + + /// Erased region, used after type-checking pub re_erased: Region<'tcx>, } @@ -876,7 +881,7 @@ impl<'tcx> CommonLifetimes<'tcx> { let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0; CommonLifetimes { - re_empty: mk(RegionKind::ReEmpty), + re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)), re_static: mk(RegionKind::ReStatic), re_erased: mk(RegionKind::ReErased), } diff --git a/src/librustc/ty/free_region_map.rs b/src/librustc/ty/free_region_map.rs index 42f506606e6..2ab12a4acbf 100644 --- a/src/librustc/ty/free_region_map.rs +++ b/src/librustc/ty/free_region_map.rs @@ -23,11 +23,61 @@ impl<'tcx> FreeRegionMap<'tcx> { // (with the exception that `'static: 'x` is not notable) pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if is_free_or_static(sub) && is_free(sup) { + if self.is_free_or_static(sub) && self.is_free(sup) { self.relation.add(sub, sup) } } + /// Tests whether `r_a <= r_b`. + /// + /// Both regions must meet `is_free_or_static`. + /// + /// Subtle: one tricky case that this code gets correct is as + /// follows. If we know that `r_b: 'static`, then this function + /// will return true, even though we don't know anything that + /// directly relates `r_a` and `r_b`. + /// + /// Also available through the `FreeRegionRelations` trait below. + pub fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>, + ) -> bool { + assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b)); + let re_static = tcx.lifetimes.re_static; + if self.check_relation(re_static, r_b) { + // `'a <= 'static` is always true, and not stored in the + // relation explicitly, so check if `'b` is `'static` (or + // equivalent to it) + true + } else { + self.check_relation(r_a, r_b) + } + } + + /// Check whether `r_a <= r_b` is found in the relation. + fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { + r_a == r_b || self.relation.contains(&r_a, &r_b) + } + + /// True for free regions other than `'static`. + pub fn is_free(&self, r: Region<'_>) -> bool { + match *r { + ty::ReEarlyBound(_) | ty::ReFree(_) => true, + _ => false, + } + } + + /// True if `r` is a free region or static of the sort that this + /// free region map can be used with. + pub fn is_free_or_static(&self, r: Region<'_>) -> bool { + match *r { + ty::ReStatic => true, + _ => self.is_free(r), + } + } + /// Computes the least-upper-bound of two free regions. In some /// cases, this is more conservative than necessary, in order to /// avoid making arbitrary choices. See @@ -39,13 +89,13 @@ impl<'tcx> FreeRegionMap<'tcx> { r_b: Region<'tcx>, ) -> Region<'tcx> { debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); - assert!(is_free(r_a)); - assert!(is_free(r_b)); + assert!(self.is_free(r_a)); + assert!(self.is_free(r_b)); let result = if r_a == r_b { r_a } else { match self.relation.postdom_upper_bound(&r_a, &r_b) { - None => tcx.mk_region(ty::ReStatic), + None => tcx.lifetimes.re_static, Some(r) => *r, } }; @@ -60,31 +110,18 @@ impl<'tcx> FreeRegionMap<'tcx> { pub trait FreeRegionRelations<'tcx> { /// Tests whether `r_a <= r_b`. Both must be free regions or /// `'static`. - fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool; + fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + shorter: ty::Region<'tcx>, + longer: ty::Region<'tcx>, + ) -> bool; } impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { - fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { - assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); - if let ty::ReStatic = r_b { - true // `'a <= 'static` is just always true, and not stored in the relation explicitly - } else { - r_a == r_b || self.relation.contains(&r_a, &r_b) - } - } -} - -fn is_free(r: Region<'_>) -> bool { - match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) => true, - _ => false, - } -} - -fn is_free_or_static(r: Region<'_>) -> bool { - match *r { - ty::ReStatic => true, - _ => is_free(r), + fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { + // invoke the "inherent method" + self.sub_free_regions(tcx, r_a, r_b) } } diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs index f5c14e73db2..0da680d1f91 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc/ty/print/pretty.rs @@ -1382,7 +1382,7 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { ty::ReVar(_) | ty::ReScope(_) | ty::ReErased => false, - ty::ReStatic | ty::ReEmpty | ty::ReClosureBound(_) => true, + ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true, } } } @@ -1464,10 +1464,14 @@ impl FmtPrinter<'_, '_, F> { p!(write("'static")); return Ok(self); } - ty::ReEmpty => { + ty::ReEmpty(ty::UniverseIndex::ROOT) => { p!(write("'")); return Ok(self); } + ty::ReEmpty(ui) => { + p!(write("'", ui)); + return Ok(self); + } // The user should never encounter these in unsubstituted form. ty::ReClosureBound(vid) => { diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 9d00d272263..acd6c959751 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -108,7 +108,7 @@ impl fmt::Debug for ty::RegionKind { ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), - ty::ReEmpty => write!(f, "ReEmpty"), + ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui), ty::ReErased => write!(f, "ReErased"), } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 0d30395d250..4c5bc3debde 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1292,11 +1292,67 @@ rustc_index::newtype_index! { pub type Region<'tcx> = &'tcx RegionKind; -/// Representation of regions. +/// Representation of (lexical) regions. Note that the NLL checker +/// uses a distinct representation of regions. For this reason, it +/// internally replaces all the regions with inference variables -- +/// the index of the variable is then used to index into internal NLL +/// data structures. See `rustc_mir::borrow_check` module for more +/// information. /// -/// Unlike types, most region variants are "fictitious", not concrete, -/// regions. Among these, `ReStatic`, `ReEmpty` and `ReScope` are the only -/// ones representing concrete regions. +/// ## The Region lattice within a given function +/// +/// In general, the (lexical, and hence deprecated) region lattice +/// looks like +/// +/// ``` +/// static ----------+-----...------+ (greatest) +/// | | | +/// early-bound and | | +/// free regions | | +/// | | | +/// scope regions | | +/// | | | +/// empty(root) placeholder(U1) | +/// | / | +/// | / placeholder(Un) +/// empty(U1) -- / +/// | / +/// ... / +/// | / +/// empty(Un) -------- (smallest) +/// ``` +/// +/// Early-bound/free regions are the named lifetimes in scope from the +/// function declaration. They have relationships to one another +/// determined based on the declared relationships from the +/// function. They all collectively outlive the scope regions. (See +/// `RegionRelations` type, and particularly +/// `crate::infer::outlives::free_region_map::FreeRegionMap`.) +/// +/// The scope regions are related to one another based on the AST +/// structure. (See `RegionRelations` type, and particularly the +/// `rustc::middle::region::ScopeTree`.) +/// +/// Note that inference variables and bound regions are not included +/// in this diagram. In the case of inference variables, they should +/// be inferred to some other region from the diagram. In the case of +/// bound regions, they are excluded because they don't make sense to +/// include -- the diagram indicates the relationship between free +/// regions. +/// +/// ## Inference variables +/// +/// During region inference, we sometimes create inference variables, +/// represented as `ReVar`. These will be inferred by the code in +/// `infer::lexical_region_resolve` to some free region from the +/// lattice above (the minimal region that meets the +/// constraints). +/// +/// During NLL checking, where regions are defined differently, we +/// also use `ReVar` -- in that case, the index is used to index into +/// the NLL region checker's data structures. The variable may in fact +/// represent either a free region or an inference variable, in that +/// case. /// /// ## Bound Regions /// @@ -1379,14 +1435,13 @@ pub enum RegionKind { /// Should not exist after typeck. RePlaceholder(ty::PlaceholderRegion), - /// Empty lifetime is for data that is never accessed. - /// Bottom in the region lattice. We treat ReEmpty somewhat - /// specially; at least right now, we do not generate instances of - /// it during the GLB computations, but rather - /// generate an error instead. This is to improve error messages. - /// The only way to get an instance of ReEmpty is to have a region - /// variable with no constraints. - ReEmpty, + /// Empty lifetime is for data that is never accessed. We tag the + /// empty lifetime with a universe -- the idea is that we don't + /// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable. + /// Therefore, the `'empty` in a universe `U` is less than all + /// regions visible from `U`, but not less than regions not visible + /// from `U`. + ReEmpty(ty::UniverseIndex), /// Erased region, used by trait selection, in MIR and during codegen. ReErased, @@ -1635,7 +1690,7 @@ impl RegionKind { RegionKind::ReStatic => true, RegionKind::ReVar(..) => false, RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(), - RegionKind::ReEmpty => false, + RegionKind::ReEmpty(_) => false, RegionKind::ReErased => false, RegionKind::ReClosureBound(..) => false, } @@ -1718,7 +1773,7 @@ impl RegionKind { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; } - ty::ReEmpty | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => { + ty::ReEmpty(_) | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } ty::ReErased => {} @@ -1728,7 +1783,7 @@ impl RegionKind { } match *self { - ty::ReStatic | ty::ReEmpty | ty::ReErased | ty::ReLateBound(..) => (), + ty::ReStatic | ty::ReEmpty(_) | ty::ReErased | ty::ReLateBound(..) => (), _ => flags = flags | TypeFlags::HAS_FREE_LOCAL_NAMES, } diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs index d14bafb44fd..1dfe97238a3 100644 --- a/src/librustc_index/vec.rs +++ b/src/librustc_index/vec.rs @@ -574,6 +574,14 @@ impl IndexVec { IndexVec { raw: vec![elem; n], _marker: PhantomData } } + /// Create an `IndexVec` with `n` elements, where the value of each + /// element is the result of `func(i)` + #[inline] + pub fn from_fn_n(func: impl FnMut(I) -> T, n: usize) -> Self { + let indices = (0..n).map(I::new); + Self::from_raw(indices.map(func).collect()) + } + #[inline] pub fn push(&mut self, d: T) -> I { let idx = I::new(self.len()); diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/src/librustc_mir/borrow_check/diagnostics/region_name.rs index 47eb2d8940a..09d61d9ad9a 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_name.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_name.rs @@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { | ty::ReScope(..) | ty::ReVar(..) | ty::RePlaceholder(..) - | ty::ReEmpty + | ty::ReEmpty(_) | ty::ReErased | ty::ReClosureBound(..) => None, } diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index 26d9cf2e045..6abca481eac 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -1108,6 +1108,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1) } + VerifyBound::IsEmpty => { + let lower_bound_scc = self.constraint_sccs.scc(lower_bound); + self.scc_values.elements_contained_in(lower_bound_scc).next().is_none() + } + VerifyBound::OutlivedBy(r) => { let r_vid = self.to_region_vid(r); self.eval_outlives(r_vid, lower_bound) diff --git a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs index 8f65a0f01c6..a3e38cd7a5f 100644 --- a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs @@ -160,7 +160,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) { - if let ty::ReEmpty = a { + // FIXME -- this is not the fix I would prefer + if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a { return; } let b = self.to_region_vid(b); @@ -175,7 +176,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' a: ty::Region<'tcx>, bound: VerifyBound<'tcx>, ) { - if let ty::ReEmpty = a { + // FIXME: I'd prefer if NLL had a notion of empty + if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a { return; } let type_test = self.verify_to_type_test(kind, a, bound); diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs index f0dc94f417c..cf8c3449d66 100644 --- a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs @@ -5,7 +5,7 @@ use rustc::mir::ConstraintCategory; use rustc::traits::query::outlives_bounds::{self, OutlivesBound}; use rustc::traits::query::type_op::{self, TypeOp}; use rustc::ty::free_region_map::FreeRegionRelations; -use rustc::ty::{self, RegionVid, Ty}; +use rustc::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_span::DUMMY_SP; use std::rc::Rc; @@ -333,7 +333,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { // `where Type:` is lowered to `where Type: 'empty` so that // we check `Type` is well formed, but there's no use for // this bound here. - if let ty::ReEmpty = r1 { + if let ty::ReEmpty(_) = r1 { return; } @@ -359,7 +359,12 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { /// over the `FreeRegionMap` from lexical regions and /// `UniversalRegions` (from NLL)`. impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> { - fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool { + fn sub_free_regions( + &self, + _tcx: TyCtxt<'tcx>, + shorter: ty::Region<'tcx>, + longer: ty::Region<'tcx>, + ) -> bool { let shorter = shorter.to_region_vid(); assert!(self.universal_regions.is_universal_region(shorter)); let longer = longer.to_region_vid(); diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index a61ab5b5e17..5a360b40d61 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -260,6 +260,16 @@ declare_lint! { }; } +declare_lint! { + pub COHERENCE_LEAK_CHECK, + Warn, + "distinct impls distinguished only by the leak-check code", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #56105 ", + edition: None, + }; +} + declare_lint! { pub DEPRECATED, Warn, @@ -515,6 +525,7 @@ declare_lint_pass! { MISSING_FRAGMENT_SPECIFIER, LATE_BOUND_LIFETIME_ARGUMENTS, ORDER_DEPENDENT_TRAIT_OBJECTS, + COHERENCE_LEAK_CHECK, DEPRECATED, UNUSED_UNSAFE, UNUSED_MUT, diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs index 70e3c4c7ab1..301ebf8adc5 100644 --- a/src/librustc_traits/chalk_context/resolvent_ops.rs +++ b/src/librustc_traits/chalk_context/resolvent_ops.rs @@ -246,9 +246,11 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> { assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var()); } - (ty::ReStatic, ty::ReStatic) - | (ty::ReErased, ty::ReErased) - | (ty::ReEmpty, ty::ReEmpty) => (), + (ty::ReStatic, ty::ReStatic) | (ty::ReErased, ty::ReErased) => (), + + (ty::ReEmpty(a_ui), ty::ReEmpty(b_ui)) => { + assert_eq!(a_ui, b_ui); + } (&ty::ReFree(a_free), &ty::ReFree(b_free)) => { assert_eq!(a_free, b_free); diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs index d60c3cfba9a..3e17b661cf4 100644 --- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs +++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs @@ -1,5 +1,5 @@ use crate::namespace::Namespace; -use rustc::traits::{self, IntercrateMode}; +use rustc::traits::{self, IntercrateMode, SkipLeakCheck}; use rustc::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -76,6 +76,9 @@ impl InherentOverlapChecker<'tcx> { impl1_def_id, impl2_def_id, IntercrateMode::Issue43355, + // We go ahead and just skip the leak check for + // inherent impls without warning. + SkipLeakCheck::Yes, |overlap| { self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); false diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 040b85b98ed..2a450f4b4e8 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2324,7 +2324,8 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // compiler/tooling bugs from not handling WF predicates. } else { let span = bound_pred.bounded_ty.span; - let predicate = ty::OutlivesPredicate(ty, tcx.mk_region(ty::ReEmpty)); + let re_root_empty = tcx.lifetimes.re_root_empty; + let predicate = ty::OutlivesPredicate(ty, re_root_empty); predicates.push(( ty::Predicate::TypeOutlives(ty::Binder::dummy(predicate)), span, diff --git a/src/librustc_typeck/outlives/utils.rs b/src/librustc_typeck/outlives/utils.rs index 2d7fc9d62e5..0cc322f8c2d 100644 --- a/src/librustc_typeck/outlives/utils.rs +++ b/src/librustc_typeck/outlives/utils.rs @@ -166,7 +166,7 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool { // // struct Bar(::Type) where Self: ; // struct Baz<'a>(&'a Self) where Self: ; - RegionKind::ReEmpty => false, + RegionKind::ReEmpty(_) => false, // These regions don't appear in types from type declarations: RegionKind::ReErased diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 5ca961ed344..6f5caea250b 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -453,7 +453,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { | ty::ReScope(..) | ty::ReVar(..) | ty::RePlaceholder(..) - | ty::ReEmpty + | ty::ReEmpty(_) | ty::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2a35ab812a5..f140f11b090 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -447,7 +447,7 @@ impl Clean> for ty::RegionKind { | ty::ReScope(..) | ty::ReVar(..) | ty::RePlaceholder(..) - | ty::ReEmpty + | ty::ReEmpty(_) | ty::ReClosureBound(_) | ty::ReErased => { debug!("cannot clean region {:?}", self); @@ -521,7 +521,7 @@ impl<'tcx> Clean> let ty::OutlivesPredicate(ref a, ref b) = *self; match (a, b) { - (ty::ReEmpty, ty::ReEmpty) => { + (ty::ReEmpty(_), ty::ReEmpty(_)) => { return None; } _ => {} @@ -539,7 +539,7 @@ impl<'tcx> Clean> for ty::OutlivesPredicate, ty: let ty::OutlivesPredicate(ref ty, ref lt) = *self; match lt { - ty::ReEmpty => return None, + ty::ReEmpty(_) => return None, _ => {} } diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr b/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr new file mode 100644 index 00000000000..6ea0b89be74 --- /dev/null +++ b/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr @@ -0,0 +1,14 @@ +error[E0592]: duplicate definitions with name `method1` + --> $DIR/coherence-inherited-subtyping.rs:14:5 + | +LL | fn method1(&self) {} + | ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1` +... +LL | fn method1(&self) {} + | -------------------- other definition for `method1` + | + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0592`. diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr b/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr new file mode 100644 index 00000000000..6ea0b89be74 --- /dev/null +++ b/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr @@ -0,0 +1,14 @@ +error[E0592]: duplicate definitions with name `method1` + --> $DIR/coherence-inherited-subtyping.rs:14:5 + | +LL | fn method1(&self) {} + | ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1` +... +LL | fn method1(&self) {} + | -------------------- other definition for `method1` + | + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0592`. diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.rs b/src/test/ui/coherence/coherence-inherited-subtyping.rs new file mode 100644 index 00000000000..8587eb77950 --- /dev/null +++ b/src/test/ui/coherence/coherence-inherited-subtyping.rs @@ -0,0 +1,21 @@ +// Test that two distinct impls which match subtypes of one another +// yield coherence errors (or not) depending on the variance. +// +// Note: This scenario is currently accepted, but as part of the +// universe transition (#56105) may eventually become an error. + +// revisions: old re + +struct Foo { + t: T, +} + +impl Foo fn(&'a u8, &'b u8) -> &'a u8> { + fn method1(&self) {} //~ ERROR duplicate definitions with name `method1` +} + +impl Foo fn(&'a u8, &'a u8) -> &'a u8> { + fn method1(&self) {} +} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-subtyping.old.stderr b/src/test/ui/coherence/coherence-subtyping.old.stderr new file mode 100644 index 00000000000..76f5cc1b782 --- /dev/null +++ b/src/test/ui/coherence/coherence-subtyping.old.stderr @@ -0,0 +1,14 @@ +warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`: + --> $DIR/coherence-subtyping.rs:16:1 + | +LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} + | ---------------------------------------------------------- first implementation here +LL | +LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` + | + = note: `#[warn(coherence_leak_check)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #56105 + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + diff --git a/src/test/ui/coherence/coherence-subtyping.re.stderr b/src/test/ui/coherence/coherence-subtyping.re.stderr new file mode 100644 index 00000000000..76f5cc1b782 --- /dev/null +++ b/src/test/ui/coherence/coherence-subtyping.re.stderr @@ -0,0 +1,14 @@ +warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`: + --> $DIR/coherence-subtyping.rs:16:1 + | +LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} + | ---------------------------------------------------------- first implementation here +LL | +LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` + | + = note: `#[warn(coherence_leak_check)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #56105 + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + diff --git a/src/test/ui/coherence/coherence-subtyping.rs b/src/test/ui/coherence/coherence-subtyping.rs index a742bf2884e..f5c1d92411b 100644 --- a/src/test/ui/coherence/coherence-subtyping.rs +++ b/src/test/ui/coherence/coherence-subtyping.rs @@ -5,16 +5,19 @@ // universe transition (#56105) may eventually become an error. // revisions: old re -// build-pass (FIXME(62277): could be check-pass?) +// check-pass trait TheTrait { - fn foo(&self) { } + fn foo(&self) {} } -impl TheTrait for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 { -} +impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { + //[re]~^ WARNING conflicting implementation + //[re]~^^ WARNING this was previously accepted by the compiler but is being phased out + //[old]~^^^ WARNING conflicting implementation + //[old]~^^^^ WARNING this was previously accepted by the compiler but is being phased out } -fn main() { } +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs index 20edfb33931..2f18f600b75 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -36,7 +36,7 @@ where T: Anything<'b, 'c>, { with_signature(cell, t, |cell, t| require(cell, t)); - //~^ ERROR associated type `>::AssocType` may not live long enough + //~^ ERROR may not live long enough } #[rustc_regions] @@ -46,7 +46,7 @@ where 'a: 'a, { with_signature(cell, t, |cell, t| require(cell, t)); - //~^ ERROR associated type `>::AssocType` may not live long enough + //~^ ERROR may not live long enough } #[rustc_regions]