From d203c13db23fe0f0d39b448e6926beaad9b550a4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 14 Jun 2022 20:18:46 -0400 Subject: [PATCH 01/18] simply the IfEq bound -- we only ever use a region the excessive generality becomes annoying later because it wouldn't implement type folding etc --- compiler/rustc_borrowck/src/region_infer/mod.rs | 11 ++++++++--- .../src/infer/lexical_region_resolve/mod.rs | 4 ++-- compiler/rustc_infer/src/infer/outlives/verify.rs | 5 ++--- .../rustc_infer/src/infer/region_constraints/mod.rs | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2c460bcb72d8b..228e88b33cf2e 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1170,9 +1170,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); match verify_bound { - VerifyBound::IfEq(test_ty, verify_bound1) => { - self.eval_if_eq(tcx, body, generic_ty, lower_bound, *test_ty, verify_bound1) - } + VerifyBound::IfEq(test_ty, verify_bound1) => self.eval_if_eq( + tcx, + body, + generic_ty, + lower_bound, + *test_ty, + &VerifyBound::OutlivedBy(*verify_bound1), + ), VerifyBound::IsEmpty => { let lower_bound_scc = self.constraint_sccs.scc(lower_bound); diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 7975b946ee5ba..c5afd3762171d 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -818,9 +818,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { min: ty::Region<'tcx>, ) -> bool { match bound { - VerifyBound::IfEq(k, b) => { + VerifyBound::IfEq(k, r) => { (var_values.normalize(self.region_rels.tcx, *k) == generic_ty) - && self.bound_is_met(b, var_values, generic_ty, min) + && self.bound_is_met(&VerifyBound::OutlivedBy(*r), var_values, generic_ty, min) } VerifyBound::OutlivedBy(r) => { diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 1c521c90686d6..17681338a639b 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -160,14 +160,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { .projection_approx_declared_bounds_from_env(projection_ty) .into_iter() .map(|ty::OutlivesPredicate(ty, r)| { - let vb = VerifyBound::OutlivedBy(r); if ty == projection_ty_as_ty { // Micro-optimize if this is an exact match (this // occurs often when there are no region variables // involved). - vb + VerifyBound::OutlivedBy(r) } else { - VerifyBound::IfEq(ty, Box::new(vb)) + VerifyBound::IfEq(ty, r) } }); diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index efe254387dcc9..d7b4f450e0f8d 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -224,7 +224,7 @@ pub enum VerifyBound<'tcx> { /// /// meaning, if the subject G is equal to `>::Item` /// (after inference), and `'a: min`, then `G: min`. - IfEq(Ty<'tcx>, Box>), + IfEq(Ty<'tcx>, Region<'tcx>), /// Given a region `R`, expands to the function: /// @@ -770,7 +770,7 @@ impl<'tcx> VerifyBound<'tcx> { pub fn cannot_hold(&self) -> bool { match self { - VerifyBound::IfEq(_, b) => b.cannot_hold(), + VerifyBound::IfEq(_, _) => false, VerifyBound::IsEmpty => false, VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), From c5ed318b22c4e6f8f894a83c3abe19070c806b66 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 05:55:05 -0400 Subject: [PATCH 02/18] implement (unused) matching solver --- compiler/rustc_borrowck/src/nll.rs | 2 +- .../rustc_borrowck/src/region_infer/mod.rs | 101 ++++++++-- .../src/infer/lexical_region_resolve/mod.rs | 21 +- compiler/rustc_infer/src/infer/mod.rs | 2 +- .../rustc_infer/src/infer/outlives/mod.rs | 1 + .../src/infer/outlives/test_type_match.rs | 179 ++++++++++++++++++ .../src/infer/region_constraints/mod.rs | 47 +++++ 7 files changed, 329 insertions(+), 24 deletions(-) create mode 100644 compiler/rustc_infer/src/infer/outlives/test_type_match.rs diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 2440ae9780d23..3a919e954a424 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -299,7 +299,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( // Solve the region constraints. let (closure_region_requirements, nll_errors) = - regioncx.solve(infcx, &body, polonius_output.clone()); + regioncx.solve(infcx, param_env, &body, polonius_output.clone()); if !nll_errors.is_empty() { // Suppress unhelpful extra errors in `infer_opaque_types`. diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 228e88b33cf2e..d553a60faef0d 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -10,7 +10,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::CRATE_HIR_ID; use rustc_index::vec::IndexVec; use rustc_infer::infer::canonical::QueryOutlivesConstraint; -use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; +use rustc_infer::infer::outlives::test_type_match; +use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, @@ -18,6 +19,7 @@ use rustc_middle::mir::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::Region; use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -46,6 +48,7 @@ pub mod values; pub struct RegionInferenceContext<'tcx> { pub var_infos: VarInfos, + /// Contains the definition for every region variable. Region /// variables are identified by their index (`RegionVid`). The /// definition contains information about where the region came @@ -559,6 +562,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(super) fn solve( &mut self, infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, polonius_output: Option>, ) -> (Option>, RegionErrors<'tcx>) { @@ -574,7 +578,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // eagerly. let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); - self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer); + self.check_type_tests( + infcx, + param_env, + body, + outlives_requirements.as_mut(), + &mut errors_buffer, + ); // In Polonius mode, the errors about missing universal region relations are in the output // and need to be emitted or propagated. Otherwise, we need to check whether the @@ -823,6 +833,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_type_tests( &self, infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, mut propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, @@ -839,7 +850,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let generic_ty = type_test.generic_kind.to_ty(tcx); if self.eval_verify_bound( - tcx, + infcx, + param_env, body, generic_ty, type_test.lower_bound, @@ -851,6 +863,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { if self.try_promote_type_test( infcx, + param_env, body, type_test, propagated_outlives_requirements, @@ -907,6 +920,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn try_promote_type_test( &self, infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, type_test: &TypeTest<'tcx>, propagated_outlives_requirements: &mut Vec>, @@ -938,7 +952,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { // where `ur` is a local bound -- we are sometimes in a // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_verify_bound(tcx, body, generic_ty, ur, &type_test.verify_bound) { + if self.eval_verify_bound( + infcx, + param_env, + body, + generic_ty, + ur, + &type_test.verify_bound, + ) { continue; } @@ -1161,7 +1182,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `point`. fn eval_verify_bound( &self, - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, @@ -1170,14 +1192,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); match verify_bound { - VerifyBound::IfEq(test_ty, verify_bound1) => self.eval_if_eq( - tcx, - body, - generic_ty, - lower_bound, - *test_ty, - &VerifyBound::OutlivedBy(*verify_bound1), - ), + VerifyBound::IfEq(test_ty, verify_bound1) => { + self.eval_if_eq(infcx, generic_ty, lower_bound, *test_ty, *verify_bound1) + } + + VerifyBound::IfEqBound(verify_if_eq_b) => { + self.eval_if_eq_bound(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b) + } VerifyBound::IsEmpty => { let lower_bound_scc = self.constraint_sccs.scc(lower_bound); @@ -1190,33 +1211,71 @@ impl<'tcx> RegionInferenceContext<'tcx> { } VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { - self.eval_verify_bound(tcx, body, generic_ty, lower_bound, verify_bound) + self.eval_verify_bound( + infcx, + param_env, + body, + generic_ty, + lower_bound, + verify_bound, + ) }), VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { - self.eval_verify_bound(tcx, body, generic_ty, lower_bound, verify_bound) + self.eval_verify_bound( + infcx, + param_env, + body, + generic_ty, + lower_bound, + verify_bound, + ) }), } } fn eval_if_eq( &self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, + infcx: &InferCtxt<'_, 'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, test_ty: Ty<'tcx>, - verify_bound: &VerifyBound<'tcx>, + verify_bound: Region<'tcx>, ) -> bool { - let generic_ty_normalized = self.normalize_to_scc_representatives(tcx, generic_ty); - let test_ty_normalized = self.normalize_to_scc_representatives(tcx, test_ty); + let generic_ty_normalized = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); + let test_ty_normalized = self.normalize_to_scc_representatives(infcx.tcx, test_ty); if generic_ty_normalized == test_ty_normalized { - self.eval_verify_bound(tcx, body, generic_ty, lower_bound, verify_bound) + let verify_bound_vid = self.to_region_vid(verify_bound); + self.eval_outlives(verify_bound_vid, lower_bound) } else { false } } + fn eval_if_eq_bound( + &self, + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + generic_ty: Ty<'tcx>, + lower_bound: RegionVid, + verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'tcx>>, + ) -> bool { + let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); + let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b); + match test_type_match::extract_verify_if_eq_bound( + infcx.tcx, + param_env, + &verify_if_eq_b, + generic_ty, + ) { + Some(r) => { + let r_vid = self.to_region_vid(r); + self.eval_outlives(r_vid, lower_bound) + } + None => false, + } + } + /// This is a conservative normalization procedure. It takes every /// free region in `value` and replaces it with the /// "representative" of its SCC (see `scc_representatives` field). diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index c5afd3762171d..1cc5f3d53c9df 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -22,6 +22,8 @@ use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; use std::fmt; +use super::outlives::test_type_match; + /// This function performs lexical region resolution given a complete /// set of constraints and variable origins. It performs a fixed-point /// iteration to find region values which satisfy all constraints, @@ -29,12 +31,13 @@ use std::fmt; /// all the variables as well as a set of errors that must be reported. #[instrument(level = "debug", skip(region_rels, var_infos, data))] pub(crate) fn resolve<'tcx>( + param_env: ty::ParamEnv<'tcx>, region_rels: &RegionRelations<'_, 'tcx>, var_infos: VarInfos, data: RegionConstraintData<'tcx>, ) -> (LexicalRegionResolutions<'tcx>, Vec>) { let mut errors = vec![]; - let mut resolver = LexicalResolver { region_rels, var_infos, data }; + let mut resolver = LexicalResolver { param_env, region_rels, var_infos, data }; let values = resolver.infer_variable_values(&mut errors); (values, errors) } @@ -100,6 +103,7 @@ struct RegionAndOrigin<'tcx> { type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>; struct LexicalResolver<'cx, 'tcx> { + param_env: ty::ParamEnv<'tcx>, region_rels: &'cx RegionRelations<'cx, 'tcx>, var_infos: VarInfos, data: RegionConstraintData<'tcx>, @@ -823,6 +827,21 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { && self.bound_is_met(&VerifyBound::OutlivedBy(*r), var_values, generic_ty, min) } + VerifyBound::IfEqBound(verify_if_eq_b) => { + match test_type_match::extract_verify_if_eq_bound( + self.tcx(), + self.param_env, + verify_if_eq_b, + generic_ty, + ) { + Some(r) => { + self.bound_is_met(&VerifyBound::OutlivedBy(r), var_values, generic_ty, min) + } + + None => false, + } + } + VerifyBound::OutlivedBy(r) => { self.sub_concrete_regions(min, var_values.normalize(self.tcx(), *r)) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 93a067cb51606..6f88b83a47321 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1290,7 +1290,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &RegionRelations::new(self.tcx, region_context, outlives_env.free_region_map()); let (lexical_region_resolutions, errors) = - lexical_region_resolve::resolve(region_rels, var_infos, data); + lexical_region_resolve::resolve(outlives_env.param_env, region_rels, var_infos, data); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index b9652e83e65be..2a085288fb7c0 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -3,6 +3,7 @@ pub mod components; pub mod env; pub mod obligations; +pub mod test_type_match; pub mod verify; use rustc_middle::traits::query::OutlivesBound; diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs new file mode 100644 index 0000000000000..99d6aabf0d6a0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -0,0 +1,179 @@ +use std::collections::hash_map::Entry; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{ + self, + error::TypeError, + relate::{self, Relate, RelateResult, TypeRelation}, + Ty, TyCtxt, +}; + +use crate::infer::region_constraints::VerifyIfEq; + +/// Given a "verify-if-eq" type test like: +/// +/// exists<'a...> { +/// verify_if_eq(some_type, bound_region) +/// } +/// +/// and the type `test_ty` that the type test is being tested against, +/// returns: +/// +/// * `None` if `some_type` cannot be made equal to `test_ty`, +/// no matter the values of the variables in `exists`. +/// * `Some(r)` with a suitable bound (typically the value of `bound_region`, modulo +/// any bound existential variables, which will be substituted) for the +/// type under test. +/// +/// NB: This function uses a simplistic, syntactic version of type equality. +/// In other words, it may spuriously return `None` even if the type-under-test +/// is in fact equal to `some_type`. In practice, though, this is used on types +/// that are either projections like `T::Item` or `T` and it works fine, but it +/// could have trouble when complex types with higher-ranked binders and the +/// like are used. This is a particular challenge since this function is invoked +/// very late in inference and hence cannot make use of the normal inference +/// machinery. +pub fn extract_verify_if_eq_bound<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + verify_if_eq_b: &ty::Binder<'tcx, VerifyIfEq<'tcx>>, + test_ty: Ty<'tcx>, +) -> Option> { + assert!(!verify_if_eq_b.has_escaping_bound_vars()); + let mut m = Match::new(tcx, param_env); + let verify_if_eq = verify_if_eq_b.skip_binder(); + m.relate(verify_if_eq.ty, test_ty).ok()?; + + if let ty::RegionKind::ReLateBound(depth, br) = verify_if_eq.bound.kind() { + assert!(depth == ty::INNERMOST); + match m.map.get(&br) { + Some(&r) => Some(r), + None => { + // If there is no mapping, then this region is unconstrained. + // In that case, we escalate to `'static`. + Some(tcx.lifetimes.re_static) + } + } + } else { + // The region does not contain any inference variables. + Some(verify_if_eq.bound) + } +} + +struct Match<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pattern_depth: ty::DebruijnIndex, + map: FxHashMap>, +} + +impl<'tcx> Match<'tcx> { + fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Match<'tcx> { + Match { tcx, param_env, pattern_depth: ty::INNERMOST, map: FxHashMap::default() } + } +} + +impl<'tcx> Match<'tcx> { + /// Creates the "Error" variant that signals "no match". + fn no_match(&self) -> RelateResult<'tcx, T> { + Err(TypeError::Mismatch) + } + + /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern + /// is already bound to a different value. + fn bind( + &mut self, + br: ty::BoundRegion, + value: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + match self.map.entry(br) { + Entry::Occupied(entry) => { + if *entry.get() == value { + Ok(value) + } else { + self.no_match() + } + } + Entry::Vacant(entry) => { + entry.insert(value); + Ok(value) + } + } + } +} + +impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { + fn tag(&self) -> &'static str { + "Match" + } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + fn a_is_expected(&self) -> bool { + true + } // irrelevant + + fn relate_with_variance>( + &mut self, + _: ty::Variance, + _: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + #[instrument(skip(self), level = "debug")] + fn regions( + &mut self, + pattern: ty::Region<'tcx>, + value: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + if let ty::RegionKind::ReLateBound(depth, br) = pattern.kind() && depth == self.pattern_depth { + self.bind(br, pattern) + } else if pattern == value { + Ok(pattern) + } else { + self.no_match() + } + } + + fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if pattern == value { + return Ok(pattern); + } else { + relate::super_relate_tys(self, pattern, value) + } + } + + fn consts( + &mut self, + pattern: ty::Const<'tcx>, + value: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), pattern, value); + if pattern == value { + return Ok(pattern); + } else { + relate::super_relate_consts(self, pattern, value) + } + } + + fn binders( + &mut self, + pattern: ty::Binder<'tcx, T>, + value: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + self.pattern_depth.shift_in(1); + let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?)); + self.pattern_depth.shift_out(1); + result + } +} diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index d7b4f450e0f8d..e0ccbb2c0f919 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -226,6 +226,8 @@ pub enum VerifyBound<'tcx> { /// (after inference), and `'a: min`, then `G: min`. IfEq(Ty<'tcx>, Region<'tcx>), + IfEqBound(ty::Binder<'tcx, VerifyIfEq<'tcx>>), + /// Given a region `R`, expands to the function: /// /// ```ignore (pseudo-rust) @@ -267,6 +269,49 @@ pub enum VerifyBound<'tcx> { AllBounds(Vec>), } +/// Given a kind K and a bound B, expands to a function like the +/// following, where `G` is the generic for which this verify +/// bound was created: +/// +/// ```ignore (pseudo-rust) +/// fn(min) -> bool { +/// if G == K { +/// B(min) +/// } else { +/// false +/// } +/// } +/// ``` +/// +/// In other words, if the generic `G` that we are checking is +/// equal to `K`, then check the associated verify bound +/// (otherwise, false). +/// +/// This is used when we have something in the environment that +/// may or may not be relevant, depending on the region inference +/// results. For example, we may have `where >::Item: 'b` in our where-clauses. If we are +/// generating the verify-bound for `>::Item`, then +/// this where-clause is only relevant if `'0` winds up inferred +/// to `'a`. +/// +/// So we would compile to a verify-bound like +/// +/// ```ignore (illustrative) +/// IfEq(>::Item, AnyRegion('a)) +/// ``` +/// +/// meaning, if the subject G is equal to `>::Item` +/// (after inference), and `'a: min`, then `G: min`. +#[derive(Debug, Copy, Clone, TypeFoldable)] +pub struct VerifyIfEq<'tcx> { + /// Type which must match the generic `G` + pub ty: Ty<'tcx>, + + /// Bound that applies if `ty` is equal. + pub bound: Region<'tcx>, +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct TwoRegions<'tcx> { a: Region<'tcx>, @@ -761,6 +806,7 @@ impl<'tcx> VerifyBound<'tcx> { pub fn must_hold(&self) -> bool { match self { VerifyBound::IfEq(..) => false, + VerifyBound::IfEqBound(..) => false, VerifyBound::OutlivedBy(re) => re.is_static(), VerifyBound::IsEmpty => false, VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), @@ -771,6 +817,7 @@ impl<'tcx> VerifyBound<'tcx> { pub fn cannot_hold(&self) -> bool { match self { VerifyBound::IfEq(_, _) => false, + VerifyBound::IfEqBound(..) => false, VerifyBound::IsEmpty => false, VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), From e260afc3ba1eeeb3bd006fb72474365698b88c47 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 06:14:48 -0400 Subject: [PATCH 03/18] simplify argument type for to take param-ty we don't really take other things --- compiler/rustc_infer/src/infer/outlives/verify.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 17681338a639b..bc6fb5ebf7c29 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -83,10 +83,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // Start with anything like `T: 'a` we can scrape from the // environment - let param_bounds = self - .declared_generic_bounds_from_env(GenericKind::Param(param_ty)) - .into_iter() - .map(|outlives| outlives.1); + let param_bounds = + self.declared_generic_bounds_from_env(param_ty).into_iter().map(|outlives| outlives.1); // Add in the default bound of fn body that applies to all in // scope type parameters: @@ -218,9 +216,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// bounds, but all the bounds it returns can be relied upon. fn declared_generic_bounds_from_env( &self, - generic: GenericKind<'tcx>, + param_ty: ty::ParamTy, ) -> Vec, ty::Region<'tcx>>> { - let generic_ty = generic.to_ty(self.tcx); + let generic_ty = param_ty.to_ty(self.tcx); self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty) } From 87c2907bca4fbb4fabb909c6da5988a97866938d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 06:35:51 -0400 Subject: [PATCH 04/18] remove compare-fn and use erased-ty --- .../rustc_infer/src/infer/outlives/verify.rs | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index bc6fb5ebf7c29..e17ec3034a561 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -123,14 +123,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { ) -> Vec, ty::Region<'tcx>>> { let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(projection_ty); - self.declared_generic_bounds_from_env_with_compare_fn(|ty| { - if let ty::Projection(..) = ty.kind() { - let erased_ty = self.tcx.erase_regions(ty); - erased_ty == erased_projection_ty - } else { - false - } - }) + self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty) } /// Searches the where-clauses in scope for regions that @@ -219,12 +212,23 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { param_ty: ty::ParamTy, ) -> Vec, ty::Region<'tcx>>> { let generic_ty = param_ty.to_ty(self.tcx); - self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty) + self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) } - fn declared_generic_bounds_from_env_with_compare_fn( + /// Searches the environment to find all bounds that apply to `erased_ty`. + /// Obviously these must be approximate -- they are in fact both *over* and + /// and *under* approximated: + /// + /// * Over-approximated because we erase regions, so + /// * Under-approximated because we look for syntactic equality and so for complex types + /// like `>::Item` or whatever we may fail to figure out + /// all the subtleties. + /// + /// In some cases, such as when `erased_ty` represents a `ty::Param`, however, + /// the result is precise. + fn declared_generic_bounds_from_env_for_erased_ty( &self, - compare_ty: impl Fn(Ty<'tcx>) -> bool, + erased_ty: Ty<'tcx>, ) -> Vec, ty::Region<'tcx>>> { let tcx = self.tcx; @@ -235,7 +239,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // like `T` and `T::Item`. It may not work as well for things // like `>::Item`. let c_b = self.param_env.caller_bounds(); - let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b.into_iter()); + let param_bounds = self.collect_outlives_from_predicate_list(erased_ty, c_b.into_iter()); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -250,18 +254,19 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // don't know that this holds from first principles. let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| { debug!( - "declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}", + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", (r, p) ); let p_ty = p.to_ty(tcx); - compare_ty(p_ty).then_some(ty::OutlivesPredicate(p_ty, r)) + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty).then_some(ty::OutlivesPredicate(p.to_ty(tcx), r)) }); param_bounds .chain(from_region_bound_pairs) .inspect(|bound| { debug!( - "declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}", + "declared_generic_bounds_from_env_for_erased_ty: result predicate = {:?}", bound ) }) @@ -341,12 +346,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// otherwise want a precise match. fn collect_outlives_from_predicate_list( &self, - compare_ty: impl Fn(Ty<'tcx>) -> bool, + erased_ty: Ty<'tcx>, predicates: impl Iterator>, ) -> impl Iterator, ty::Region<'tcx>>> { + let tcx = self.tcx; predicates .filter_map(|p| p.to_opt_type_outlives()) .filter_map(|p| p.no_bound_vars()) - .filter(move |p| compare_ty(p.0)) + .filter(move |p| tcx.erase_regions(p.0) == erased_ty) } } From 10f0f66d454c9a04e6f9703e66cbfe6ed109655a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 09:45:19 -0400 Subject: [PATCH 05/18] improved debug output --- compiler/rustc_infer/src/infer/outlives/obligations.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 2aa535da0e54c..cd9f020abd0fe 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -318,17 +318,13 @@ where self.delegate.push_verify(origin, generic, region, verify_bound); } + #[tracing::instrument(level = "Debug", skip(self))] fn projection_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, ) { - debug!( - "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", - region, projection_ty, origin - ); - // This case is thorny for inference. The fundamental problem is // that there are many cases where we have choice, and inference // doesn't like choice (the current region inference in @@ -437,6 +433,7 @@ where // even though a satisfactory solution exists. let generic = GenericKind::Projection(projection_ty); let verify_bound = self.verify_bound.generic_bound(generic); + debug!("projection_must_outlive: pushing verify_bound={:?}", verify_bound,); self.delegate.push_verify(origin, generic, region, verify_bound); } } From b7b3d2cee0e371fdecf9726ab949a24ead3dcb97 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 08:46:19 -0400 Subject: [PATCH 06/18] generalize the outlives obligation code The code now accepts `Binder` instead of just `OutlivesPredicate` and thus exercises the new, generalized `IfEqBound` codepaths. Note though that we never *produce* Binder, so we are only testing a subset of those codepaths that excludes actual higher-ranked outlives bounds. --- .../src/infer/outlives/obligations.rs | 36 ++++++++--- .../src/infer/outlives/test_type_match.rs | 26 +++++++- .../rustc_infer/src/infer/outlives/verify.rs | 63 +++++++++++++------ src/test/ui/borrowck/issue-71546.rs | 14 ++--- src/test/ui/borrowck/issue-71546.stderr | 59 ----------------- .../generic-associated-types/issue-86483.rs | 8 ++- .../issue-86483.stderr | 50 --------------- ...sue-88586-hr-self-outlives-in-trait-def.rs | 8 ++- ...88586-hr-self-outlives-in-trait-def.stderr | 19 ------ 9 files changed, 109 insertions(+), 174 deletions(-) delete mode 100644 src/test/ui/borrowck/issue-71546.stderr delete mode 100644 src/test/ui/generic-associated-types/issue-86483.stderr delete mode 100644 src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.stderr diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index cd9f020abd0fe..42a173bf092b7 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -359,13 +359,21 @@ where // #55756) in cases where you have e.g., `>::Item: // 'a` in the environment but `trait Foo<'b> { type Item: 'b // }` in the trait definition. - approx_env_bounds.retain(|bound| match *bound.0.kind() { - ty::Projection(projection_ty) => self - .verify_bound - .projection_declared_bounds_from_trait(projection_ty) - .all(|r| r != bound.1), - - _ => panic!("expected only projection types from env, not {:?}", bound.0), + approx_env_bounds.retain(|bound_outlives| { + // OK to skip binder because we only manipulate and compare against other + // values from the same inder. e.g. if we have (e.g.) `for<'a> >::Item: 'a` + // in `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region. + // If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait` + // will be invoked with `['b => ^1]` and so we will get `^1` returned. + let bound = bound_outlives.skip_binder(); + match *bound.0.kind() { + ty::Projection(projection_ty) => self + .verify_bound + .projection_declared_bounds_from_trait(projection_ty) + .all(|r| r != bound.1), + + _ => panic!("expected only projection types from env, not {:?}", bound.0), + } }); // If declared bounds list is empty, the only applicable rule is @@ -416,8 +424,16 @@ where if !trait_bounds.is_empty() && trait_bounds[1..] .iter() - .chain(approx_env_bounds.iter().map(|b| &b.1)) - .all(|b| *b == trait_bounds[0]) + .map(|r| Some(*r)) + .chain( + // NB: The environment may contain `for<'a> T: 'a` style bounds. + // In that case, we don't know if they are equal to the trait bound + // or not (since we don't *know* whether the environment bound even applies), + // so just map to `None` here if there are bound vars, ensuring that + // the call to `all` will fail below. + approx_env_bounds.iter().map(|b| b.map_bound(|b| b.1).no_bound_vars()), + ) + .all(|b| b == Some(trait_bounds[0])) { let unique_bound = trait_bounds[0]; debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound); @@ -433,7 +449,7 @@ where // even though a satisfactory solution exists. let generic = GenericKind::Projection(projection_ty); let verify_bound = self.verify_bound.generic_bound(generic); - debug!("projection_must_outlive: pushing verify_bound={:?}", verify_bound,); + debug!("projection_must_outlive: pushing {:?}", verify_bound); self.delegate.push_verify(origin, generic, region, verify_bound); } } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 99d6aabf0d6a0..bdd31dafd90d2 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -34,6 +34,7 @@ use crate::infer::region_constraints::VerifyIfEq; /// like are used. This is a particular challenge since this function is invoked /// very late in inference and hence cannot make use of the normal inference /// machinery. +#[tracing::instrument(level = "Debug", skip(tcx, param_env))] pub fn extract_verify_if_eq_bound<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -61,6 +62,25 @@ pub fn extract_verify_if_eq_bound<'tcx>( } } +/// True if a (potentially higher-ranked) outlives +#[tracing::instrument(level = "Debug", skip(tcx, param_env))] +pub(super) fn can_match_erased_ty<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + outlives_predicate: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>, + erased_ty: Ty<'tcx>, +) -> bool { + assert!(!outlives_predicate.has_escaping_bound_vars()); + let erased_outlives_predicate = tcx.erase_regions(outlives_predicate); + let outlives_ty = erased_outlives_predicate.skip_binder().0; + if outlives_ty == erased_ty { + // pointless micro-optimization + true + } else { + Match::new(tcx, param_env).relate(outlives_ty, erased_ty).is_ok() + } +} + struct Match<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -82,6 +102,7 @@ impl<'tcx> Match<'tcx> { /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern /// is already bound to a different value. + #[tracing::instrument(level = "Debug", skip(self))] fn bind( &mut self, br: ty::BoundRegion, @@ -133,8 +154,9 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { pattern: ty::Region<'tcx>, value: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("self.pattern_depth = {:?}", self.pattern_depth); if let ty::RegionKind::ReLateBound(depth, br) = pattern.kind() && depth == self.pattern_depth { - self.bind(br, pattern) + self.bind(br, value) } else if pattern == value { Ok(pattern) } else { @@ -142,6 +164,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { } } + #[instrument(skip(self), level = "debug")] fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { if pattern == value { return Ok(pattern); @@ -150,6 +173,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { } } + #[instrument(skip(self), level = "debug")] fn consts( &mut self, pattern: ty::Const<'tcx>, diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index e17ec3034a561..a88bcdaef582d 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,4 +1,5 @@ use crate::infer::outlives::env::RegionBoundPairs; +use crate::infer::region_constraints::VerifyIfEq; use crate::infer::{GenericKind, VerifyBound}; use rustc_data_structures::captures::Captures; use rustc_data_structures::sso::SsoHashSet; @@ -82,25 +83,39 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { debug!("param_bound(param_ty={:?})", param_ty); // Start with anything like `T: 'a` we can scrape from the - // environment - let param_bounds = - self.declared_generic_bounds_from_env(param_ty).into_iter().map(|outlives| outlives.1); + // environment. If the environment contains something like + // `for<'a> T: 'a`, then we know that `T` outlives everything. + let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty); + let mut param_bounds = vec![]; + for declared_bound in declared_bounds_from_env { + let bound_region = declared_bound.map_bound(|outlives| outlives.1); + if let Some(region) = bound_region.no_bound_vars() { + // This is `T: 'a` for some free region `'a`. + param_bounds.push(VerifyBound::OutlivedBy(region)); + } else { + // This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here. + return VerifyBound::AllBounds(vec![]); + } + } // Add in the default bound of fn body that applies to all in // scope type parameters: - let param_bounds = param_bounds.chain(self.implicit_region_bound); - - let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect(); + if let Some(r) = self.implicit_region_bound { + param_bounds.push(VerifyBound::OutlivedBy(r)); + } - if any_bounds.is_empty() { + if param_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 param_bounds.len() == 1 { + // Micro-opt: no need to store the vector if it's just len 1 + param_bounds.pop().unwrap() } 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) + VerifyBound::AnyBound(param_bounds) } } @@ -120,7 +135,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub fn projection_approx_declared_bounds_from_env( &self, projection_ty: ty::ProjectionTy<'tcx>, - ) -> Vec, ty::Region<'tcx>>> { + ) -> Vec, ty::Region<'tcx>>>> { let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(projection_ty); self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty) @@ -150,14 +165,15 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { let env_bounds = self .projection_approx_declared_bounds_from_env(projection_ty) .into_iter() - .map(|ty::OutlivesPredicate(ty, r)| { - if ty == projection_ty_as_ty { + .map(|binder| { + if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty { // Micro-optimize if this is an exact match (this // occurs often when there are no region variables // involved). VerifyBound::OutlivedBy(r) } else { - VerifyBound::IfEq(ty, r) + let verify_if_eq_b = binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }); + VerifyBound::IfEqBound(verify_if_eq_b) } }); @@ -210,7 +226,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env( &self, param_ty: ty::ParamTy, - ) -> Vec, ty::Region<'tcx>>> { + ) -> Vec, ty::Region<'tcx>>>> { let generic_ty = param_ty.to_ty(self.tcx); self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) } @@ -229,7 +245,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, - ) -> Vec, ty::Region<'tcx>>> { + ) -> Vec, ty::Region<'tcx>>>> { let tcx = self.tcx; // To start, collect bounds from user environment. Note that @@ -259,7 +275,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { ); let p_ty = p.to_ty(tcx); let erased_p_ty = self.tcx.erase_regions(p_ty); - (erased_p_ty == erased_ty).then_some(ty::OutlivesPredicate(p.to_ty(tcx), r)) + (erased_p_ty == erased_ty) + .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r))) }); param_bounds @@ -348,11 +365,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { &self, erased_ty: Ty<'tcx>, predicates: impl Iterator>, - ) -> impl Iterator, ty::Region<'tcx>>> { + ) -> impl Iterator, ty::Region<'tcx>>>> + { let tcx = self.tcx; - predicates - .filter_map(|p| p.to_opt_type_outlives()) - .filter_map(|p| p.no_bound_vars()) - .filter(move |p| tcx.erase_regions(p.0) == erased_ty) + let param_env = self.param_env; + predicates.filter_map(|p| p.to_opt_type_outlives()).filter(move |outlives_predicate| { + super::test_type_match::can_match_erased_ty( + tcx, + param_env, + *outlives_predicate, + erased_ty, + ) + }) } } diff --git a/src/test/ui/borrowck/issue-71546.rs b/src/test/ui/borrowck/issue-71546.rs index b20c39193de39..42100edeaa712 100644 --- a/src/test/ui/borrowck/issue-71546.rs +++ b/src/test/ui/borrowck/issue-71546.rs @@ -1,4 +1,8 @@ // Regression test for #71546. +// +// Made to pass as part of fixing #98095. +// +// check-pass pub fn serialize_as_csv(value: &V) -> Result where @@ -6,15 +10,7 @@ where for<'a> &'a V: IntoIterator, for<'a> <&'a V as IntoIterator>::Item: ToString + 'static, { - let csv_str: String = value - //~^ ERROR higher-ranked lifetime error - //~| ERROR higher-ranked lifetime error - //~| ERROR higher-ranked lifetime error - .into_iter() - .map(|elem| elem.to_string()) - //~^ ERROR higher-ranked lifetime error - .collect::(); - //~^ ERROR higher-ranked lifetime error + let csv_str: String = value.into_iter().map(|elem| elem.to_string()).collect::(); Ok(csv_str) } diff --git a/src/test/ui/borrowck/issue-71546.stderr b/src/test/ui/borrowck/issue-71546.stderr deleted file mode 100644 index b8d79f0939b41..0000000000000 --- a/src/test/ui/borrowck/issue-71546.stderr +++ /dev/null @@ -1,59 +0,0 @@ -error: higher-ranked lifetime error - --> $DIR/issue-71546.rs:9:27 - | -LL | let csv_str: String = value - | ___________________________^ -LL | | -LL | | -LL | | -LL | | .into_iter() -LL | | .map(|elem| elem.to_string()) - | |_____________________________________^ - | - = note: could not prove for<'r> [closure@$DIR/issue-71546.rs:14:14: 14:37] well-formed - -error: higher-ranked lifetime error - --> $DIR/issue-71546.rs:9:27 - | -LL | let csv_str: String = value - | ___________________________^ -LL | | -LL | | -LL | | -LL | | .into_iter() -LL | | .map(|elem| elem.to_string()) - | |_____________________________________^ - | - = note: could not prove for<'r, 's> Map<<&'r V as IntoIterator>::IntoIter, [closure@$DIR/issue-71546.rs:14:14: 14:37]> well-formed - -error: higher-ranked lifetime error - --> $DIR/issue-71546.rs:9:27 - | -LL | let csv_str: String = value - | ___________________________^ -LL | | -LL | | -LL | | -... | -LL | | -LL | | .collect::(); - | |____________________________^ - | - = note: could not prove for<'r, 's> Map<<&'r V as IntoIterator>::IntoIter, [closure@$DIR/issue-71546.rs:14:14: 14:37]> well-formed - -error: higher-ranked lifetime error - --> $DIR/issue-71546.rs:14:14 - | -LL | .map(|elem| elem.to_string()) - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: could not prove for<'a> <&'a V as IntoIterator>::Item: 'static - -error: higher-ranked lifetime error - --> $DIR/issue-71546.rs:16:10 - | -LL | .collect::(); - | ^^^^^^^ - -error: aborting due to 5 previous errors - diff --git a/src/test/ui/generic-associated-types/issue-86483.rs b/src/test/ui/generic-associated-types/issue-86483.rs index a8b54c354e3f3..07dd0bffd4685 100644 --- a/src/test/ui/generic-associated-types/issue-86483.rs +++ b/src/test/ui/generic-associated-types/issue-86483.rs @@ -1,14 +1,16 @@ // Regression test of #86483. +// +// Made to pass as part of fixing #98095. +// +// check-pass #![feature(generic_associated_types)] -pub trait IceIce //~ ERROR: the parameter type `T` may not live long enough +pub trait IceIce where for<'a> T: 'a, { type Ice<'v>: IntoIterator; - //~^ ERROR: the parameter type `T` may not live long enough - //~| ERROR: the parameter type `T` may not live long enough } fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-86483.stderr b/src/test/ui/generic-associated-types/issue-86483.stderr deleted file mode 100644 index a13dc043dc52b..0000000000000 --- a/src/test/ui/generic-associated-types/issue-86483.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error[E0311]: the parameter type `T` may not live long enough - --> $DIR/issue-86483.rs:5:1 - | -LL | / pub trait IceIce -LL | | where -LL | | for<'a> T: 'a, -LL | | { -... | -LL | | -LL | | } - | |_^ - | - = note: ...so that the type `T` will meet its required lifetime bounds... -note: ...that is required by this bound - --> $DIR/issue-86483.rs:7:16 - | -LL | for<'a> T: 'a, - | ^^ -help: consider adding an explicit lifetime bound... - | -LL | for<'a> T: 'a + 'a, - | ++++ - -error[E0311]: the parameter type `T` may not live long enough - --> $DIR/issue-86483.rs:9:5 - | -LL | type Ice<'v>: IntoIterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... - | -note: ...that is required by this bound - --> $DIR/issue-86483.rs:7:16 - | -LL | for<'a> T: 'a, - | ^^ -help: consider adding an explicit lifetime bound... - | -LL | for<'a> T: 'a + 'a, - | ++++ - -error[E0309]: the parameter type `T` may not live long enough - --> $DIR/issue-86483.rs:9:32 - | -LL | type Ice<'v>: IntoIterator; - | ^^^^^^^^^^^^ - help: consider adding a where clause: `where T: 'v` - | | - | ...so that the reference type `&'v T` does not outlive the data it points at - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.rs b/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.rs index b50f56b03d9cd..92b7c5deb812e 100644 --- a/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.rs +++ b/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.rs @@ -1,10 +1,12 @@ // Regression test for #88586: a higher-ranked outlives bound on Self in a trait // definition caused an ICE when debug_assertions were enabled. // -// FIXME: The error output in the absence of the ICE is unhelpful; this should be improved. +// Made to pass as part of fixing #98095. +// +// check-pass -trait A where for<'a> Self: 'a -//~^ ERROR the parameter type `Self` may not live long enough +trait A where + for<'a> Self: 'a, { } diff --git a/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.stderr b/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.stderr deleted file mode 100644 index 18618ffcc86dc..0000000000000 --- a/src/test/ui/higher-rank-trait-bounds/issue-88586-hr-self-outlives-in-trait-def.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0311]: the parameter type `Self` may not live long enough - --> $DIR/issue-88586-hr-self-outlives-in-trait-def.rs:6:1 - | -LL | / trait A where for<'a> Self: 'a -LL | | -LL | | { -LL | | } - | |_^ - | - = help: consider adding an explicit lifetime bound `Self: 'a`... - = note: ...so that the type `Self` will meet its required lifetime bounds... -note: ...that is required by this bound - --> $DIR/issue-88586-hr-self-outlives-in-trait-def.rs:6:29 - | -LL | trait A where for<'a> Self: 'a - | ^^ - -error: aborting due to previous error - From 12912b9cde4791dead52e49ae87f80c579bc0cd5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 17:28:05 -0400 Subject: [PATCH 07/18] fix universes in the NLL type tests In the NLL code, we were not accommodating universes in the `type_test` logic. This led to issue 98095. --- .../rustc_borrowck/src/region_infer/mod.rs | 25 ++++++++++++++++++- src/test/ui/nll/type-test-universe.rs | 21 ++++++++++++++++ src/test/ui/nll/type-test-universe.stderr | 16 ++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/nll/type-test-universe.rs create mode 100644 src/test/ui/nll/type-test-universe.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index d553a60faef0d..4830eb3f464a7 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1342,6 +1342,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { let sub_region_scc = self.constraint_sccs.scc(sub_region); let sup_region_scc = self.constraint_sccs.scc(sup_region); + // If we are checking that `'sup: 'sub`, and `'sub` contains + // some placeholder that `'sup` cannot name, then this is only + // true if `'sup` outlives static. + if !self.universe_compatible(sub_region_scc, sup_region_scc) { + debug!( + "eval_outlives: sub universe `{sub_region_scc:?}` is not nameable \ + by super `{sup_region_scc:?}`, promoting to static", + ); + + return self.eval_outlives(sup_region, self.universal_regions.fr_static); + } + // Both the `sub_region` and `sup_region` consist of the union // of some number of universal regions (along with the union // of various points in the CFG; ignore those points for @@ -1356,6 +1368,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); if !universal_outlives { + debug!( + "eval_outlives: returning false because sub region contains a universal region not present in super" + ); return false; } @@ -1364,10 +1379,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { if self.universal_regions.is_universal_region(sup_region) { // Micro-opt: universal regions contain all points. + debug!( + "eval_outlives: returning true because super is universal and hence contains all points" + ); return true; } - self.scc_values.contains_points(sup_region_scc, sub_region_scc) + let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc); + debug!( + "eval_outlives: returning {} because of comparison between points in sup/sub", + result + ); + result } /// Once regions have been propagated, this method is used to see diff --git a/src/test/ui/nll/type-test-universe.rs b/src/test/ui/nll/type-test-universe.rs new file mode 100644 index 0000000000000..f9801c07d7bcb --- /dev/null +++ b/src/test/ui/nll/type-test-universe.rs @@ -0,0 +1,21 @@ +// Regression test for #98095: make sure that +// we detect that S needs to outlive 'static. + +fn outlives_forall() +where + for<'u> T: 'u, +{ +} + +fn test1() { + outlives_forall::(); + //~^ ERROR `S` does not live long enough +} + +struct Value<'a>(&'a ()); +fn test2<'a>() { + outlives_forall::>(); + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/type-test-universe.stderr b/src/test/ui/nll/type-test-universe.stderr new file mode 100644 index 0000000000000..242486c360a80 --- /dev/null +++ b/src/test/ui/nll/type-test-universe.stderr @@ -0,0 +1,16 @@ +error: `S` does not live long enough + --> $DIR/type-test-universe.rs:11:5 + | +LL | outlives_forall::(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/type-test-universe.rs:17:5 + | +LL | fn test2<'a>() { + | -- lifetime `'a` defined here +LL | outlives_forall::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + From ad25ee09c70e3c87cc88cf93496dd97ef971bab2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 10:49:09 -0400 Subject: [PATCH 08/18] mark issue-91139 and issue-92096 as FIXME These were "fixed" as part of switching on NLL but seems to be due to another problem. Preliminary investigation suggests they are both PROBABLY "implied bounds" related. --- .../generic-associated-types/issue-91139.rs | 18 ++++++- .../issue-91139.stderr | 50 +++++++++++++++++++ .../generic-associated-types/issue-92096.rs | 9 +++- .../issue-92096.stderr | 8 +++ 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/generic-associated-types/issue-91139.stderr create mode 100644 src/test/ui/generic-associated-types/issue-92096.stderr diff --git a/src/test/ui/generic-associated-types/issue-91139.rs b/src/test/ui/generic-associated-types/issue-91139.rs index 03dc8ef93fe3a..092fa939c308d 100644 --- a/src/test/ui/generic-associated-types/issue-91139.rs +++ b/src/test/ui/generic-associated-types/issue-91139.rs @@ -1,5 +1,3 @@ -//check-pass - #![feature(generic_associated_types)] trait Foo { @@ -16,6 +14,22 @@ impl Foo for () { fn foo() { let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + //~^ ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + //~| ERROR `T` does not live long enough + // + // FIXME: This error is bogus, but it arises because we try to validate + // that `<() as Foo>::Type<'a>` is valid, which requires proving + // that `T: 'a`. Since `'a` is higher-ranked, this becomes + // `for<'a> T: 'a`, which is not true. Of course, the error is bogus + // because there *ought* to be an implied bound stating that `'a` is + // not any lifetime but specifically + // "some `'a` such that `<() as Foo>::Type<'a>" is valid". } pub fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-91139.stderr b/src/test/ui/generic-associated-types/issue-91139.stderr new file mode 100644 index 0000000000000..6c5092978c84b --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-91139.stderr @@ -0,0 +1,50 @@ +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:12 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:12 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:12 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:12 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:58 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:58 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:58 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ + +error: `T` does not live long enough + --> $DIR/issue-91139.rs:16:58 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/generic-associated-types/issue-92096.rs b/src/test/ui/generic-associated-types/issue-92096.rs index bfe0fc15fd310..59dd7ea556345 100644 --- a/src/test/ui/generic-associated-types/issue-92096.rs +++ b/src/test/ui/generic-associated-types/issue-92096.rs @@ -1,5 +1,4 @@ // edition:2018 -// check-pass #![feature(generic_associated_types)] @@ -18,6 +17,14 @@ where C: Client + Send + Sync, { async move { c.connect().await } + //~^ ERROR `C` does not live long enough + // + // FIXME. This is because we infer at some point a value of + // + // impl Future::Connection<'_>> + // + // and then we somehow fail the WF check because `where C: 'a` is not known, + // but I'm not entirely sure how that comes about. } fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-92096.stderr b/src/test/ui/generic-associated-types/issue-92096.stderr new file mode 100644 index 0000000000000..ca61a0f435e93 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-92096.stderr @@ -0,0 +1,8 @@ +error: `C` does not live long enough + --> $DIR/issue-92096.rs:19:5 + | +LL | async move { c.connect().await } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From b39ba21fcbf262b94bc8b7c20310fc7fb923d53c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jun 2022 11:02:46 -0400 Subject: [PATCH 09/18] tests H.R. `T: 'a` bounds proving themselves --- src/test/ui/regions/forall-wf-ref-reflexive.rs | 18 ++++++++++++++++++ .../ui/regions/forall-wf-ref-reflexive.stderr | 8 ++++++++ src/test/ui/regions/forall-wf-reflexive.rs | 15 +++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/test/ui/regions/forall-wf-ref-reflexive.rs create mode 100644 src/test/ui/regions/forall-wf-ref-reflexive.stderr create mode 100644 src/test/ui/regions/forall-wf-reflexive.rs diff --git a/src/test/ui/regions/forall-wf-ref-reflexive.rs b/src/test/ui/regions/forall-wf-ref-reflexive.rs new file mode 100644 index 0000000000000..9c37d72d56b64 --- /dev/null +++ b/src/test/ui/regions/forall-wf-ref-reflexive.rs @@ -0,0 +1,18 @@ +// Test that we consider `for<'a> &'a T: 'a` to be sufficient to prove +// that `for<'a> &'a T: 'a`. +// +// FIXME. Except we don't! + +#![allow(warnings)] + +fn self_wf2() +where + for<'a> &'a T: 'a, +{ + self_wf2::(); + //~^ ERROR `T` does not live long enough + // + // FIXME. This ought to be accepted, presumably. +} + +fn main() {} diff --git a/src/test/ui/regions/forall-wf-ref-reflexive.stderr b/src/test/ui/regions/forall-wf-ref-reflexive.stderr new file mode 100644 index 0000000000000..3d059ccec7296 --- /dev/null +++ b/src/test/ui/regions/forall-wf-ref-reflexive.stderr @@ -0,0 +1,8 @@ +error: `T` does not live long enough + --> $DIR/forall-wf-ref-reflexive.rs:12:5 + | +LL | self_wf2::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/regions/forall-wf-reflexive.rs b/src/test/ui/regions/forall-wf-reflexive.rs new file mode 100644 index 0000000000000..8e6b8224b3186 --- /dev/null +++ b/src/test/ui/regions/forall-wf-reflexive.rs @@ -0,0 +1,15 @@ +// Test that we consider `for<'a> T: 'a` to be sufficient to prove +// that `for<'a> T: 'a`. +// +// check-pass + +#![allow(warnings)] + +fn self_wf1() +where + for<'a> T: 'a, +{ + self_wf1::(); +} + +fn main() {} From 893b919e64b59c04dbd7c88e74ba138ff8e22a64 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:28:46 -0400 Subject: [PATCH 10/18] remove VerifyBound::IfEq variant --- .../rustc_borrowck/src/region_infer/mod.rs | 23 ------- .../src/infer/lexical_region_resolve/mod.rs | 5 -- .../src/infer/region_constraints/mod.rs | 62 +++++++++---------- 3 files changed, 31 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 4830eb3f464a7..5604516606015 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -19,7 +19,6 @@ use rustc_middle::mir::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::Region; use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -1192,10 +1191,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); match verify_bound { - VerifyBound::IfEq(test_ty, verify_bound1) => { - self.eval_if_eq(infcx, generic_ty, lower_bound, *test_ty, *verify_bound1) - } - VerifyBound::IfEqBound(verify_if_eq_b) => { self.eval_if_eq_bound(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b) } @@ -1234,24 +1229,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - fn eval_if_eq( - &self, - infcx: &InferCtxt<'_, 'tcx>, - generic_ty: Ty<'tcx>, - lower_bound: RegionVid, - test_ty: Ty<'tcx>, - verify_bound: Region<'tcx>, - ) -> bool { - let generic_ty_normalized = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); - let test_ty_normalized = self.normalize_to_scc_representatives(infcx.tcx, test_ty); - if generic_ty_normalized == test_ty_normalized { - let verify_bound_vid = self.to_region_vid(verify_bound); - self.eval_outlives(verify_bound_vid, lower_bound) - } else { - false - } - } - fn eval_if_eq_bound( &self, infcx: &InferCtxt<'_, 'tcx>, diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 1cc5f3d53c9df..51943f9f38c6d 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -822,11 +822,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { min: ty::Region<'tcx>, ) -> bool { match bound { - VerifyBound::IfEq(k, r) => { - (var_values.normalize(self.region_rels.tcx, *k) == generic_ty) - && self.bound_is_met(&VerifyBound::OutlivedBy(*r), var_values, generic_ty, min) - } - VerifyBound::IfEqBound(verify_if_eq_b) => { match test_type_match::extract_verify_if_eq_bound( self.tcx(), diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index e0ccbb2c0f919..ab76f41414ac6 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -190,42 +190,44 @@ pub enum GenericKind<'tcx> { /// This is described with an `AnyRegion('a, 'b)` node. #[derive(Debug, Clone)] pub enum VerifyBound<'tcx> { - /// Given a kind K and a bound B, expands to a function like the - /// following, where `G` is the generic for which this verify - /// bound was created: + /// This is a "conditional bound" that checks the result of inference + /// and supplies a bound if it ended up being relevant. It's used in situations + /// like this: /// - /// ```ignore (pseudo-rust) - /// fn(min) -> bool { - /// if G == K { - /// B(min) - /// } else { - /// false - /// } - /// } + /// ```rust + /// fn foo<'a, 'b, T: SomeTrait<'a>> + /// where + /// >::Item: 'b /// ``` /// - /// In other words, if the generic `G` that we are checking is - /// equal to `K`, then check the associated verify bound - /// (otherwise, false). - /// - /// This is used when we have something in the environment that - /// may or may not be relevant, depending on the region inference - /// results. For example, we may have `where >::Item: 'b` in our where-clauses. If we are - /// generating the verify-bound for `>::Item`, then - /// this where-clause is only relevant if `'0` winds up inferred - /// to `'a`. + /// If we have an obligation like `>::Item: 'c`, then + /// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds + /// up being equal to `'a`, then the where-clauses on function applies, and + /// in that case we can show `'b: 'c`. But if `'?x` winds up being something + /// else, the bound isn't relevant. /// - /// So we would compile to a verify-bound like + /// More abstractly, this function takes a `Binder`. The binder + /// represents an existential binder -- i.e., if you have something like /// - /// ```ignore (illustrative) - /// IfEq(>::Item, AnyRegion('a)) + /// ```rust + /// where for<'a> ::Item: 'a /// ``` /// - /// meaning, if the subject G is equal to `>::Item` - /// (after inference), and `'a: min`, then `G: min`. - IfEq(Ty<'tcx>, Region<'tcx>), - + /// then the `for<'a>` corresponds to the binder. The idea is that we have + /// to find some instantiation of `'a` that can make `>::Item` + /// equal to the final value of `G`, the generic we are checking. + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// exists<'a> { + /// if G == K { + /// B(min) + /// } else { + /// false + /// } + /// } + /// } + /// ``` IfEqBound(ty::Binder<'tcx, VerifyIfEq<'tcx>>), /// Given a region `R`, expands to the function: @@ -805,7 +807,6 @@ impl<'tcx> GenericKind<'tcx> { impl<'tcx> VerifyBound<'tcx> { pub fn must_hold(&self) -> bool { match self { - VerifyBound::IfEq(..) => false, VerifyBound::IfEqBound(..) => false, VerifyBound::OutlivedBy(re) => re.is_static(), VerifyBound::IsEmpty => false, @@ -816,7 +817,6 @@ impl<'tcx> VerifyBound<'tcx> { pub fn cannot_hold(&self) -> bool { match self { - VerifyBound::IfEq(_, _) => false, VerifyBound::IfEqBound(..) => false, VerifyBound::IsEmpty => false, VerifyBound::OutlivedBy(_) => false, From 27e0f7a8864ebd64eda4737f74b1324a98da2e95 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:35:47 -0400 Subject: [PATCH 11/18] rename IfEqBound to IfEq --- compiler/rustc_borrowck/src/region_infer/mod.rs | 8 ++++---- .../rustc_infer/src/infer/lexical_region_resolve/mod.rs | 4 ++-- .../rustc_infer/src/infer/outlives/test_type_match.rs | 2 +- compiler/rustc_infer/src/infer/outlives/verify.rs | 2 +- compiler/rustc_infer/src/infer/region_constraints/mod.rs | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5604516606015..d45cdc0c990f7 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1191,8 +1191,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); match verify_bound { - VerifyBound::IfEqBound(verify_if_eq_b) => { - self.eval_if_eq_bound(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b) + VerifyBound::IfEq(verify_if_eq_b) => { + self.eval_if_eq(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b) } VerifyBound::IsEmpty => { @@ -1229,7 +1229,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - fn eval_if_eq_bound( + fn eval_if_eq( &self, infcx: &InferCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -1239,7 +1239,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> bool { let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b); - match test_type_match::extract_verify_if_eq_bound( + match test_type_match::extract_verify_if_eq( infcx.tcx, param_env, &verify_if_eq_b, diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 51943f9f38c6d..9fa374a53fcae 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -822,8 +822,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { min: ty::Region<'tcx>, ) -> bool { match bound { - VerifyBound::IfEqBound(verify_if_eq_b) => { - match test_type_match::extract_verify_if_eq_bound( + VerifyBound::IfEq(verify_if_eq_b) => { + match test_type_match::extract_verify_if_eq( self.tcx(), self.param_env, verify_if_eq_b, diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index bdd31dafd90d2..efb889cdefbd3 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -35,7 +35,7 @@ use crate::infer::region_constraints::VerifyIfEq; /// very late in inference and hence cannot make use of the normal inference /// machinery. #[tracing::instrument(level = "Debug", skip(tcx, param_env))] -pub fn extract_verify_if_eq_bound<'tcx>( +pub fn extract_verify_if_eq<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, verify_if_eq_b: &ty::Binder<'tcx, VerifyIfEq<'tcx>>, diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index a88bcdaef582d..ce3da267b00f5 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -173,7 +173,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { VerifyBound::OutlivedBy(r) } else { let verify_if_eq_b = binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }); - VerifyBound::IfEqBound(verify_if_eq_b) + VerifyBound::IfEq(verify_if_eq_b) } }); diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index ab76f41414ac6..f93c9a31be675 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -228,7 +228,7 @@ pub enum VerifyBound<'tcx> { /// } /// } /// ``` - IfEqBound(ty::Binder<'tcx, VerifyIfEq<'tcx>>), + IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>), /// Given a region `R`, expands to the function: /// @@ -807,7 +807,7 @@ impl<'tcx> GenericKind<'tcx> { impl<'tcx> VerifyBound<'tcx> { pub fn must_hold(&self) -> bool { match self { - VerifyBound::IfEqBound(..) => false, + VerifyBound::IfEq(..) => false, VerifyBound::OutlivedBy(re) => re.is_static(), VerifyBound::IsEmpty => false, VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), @@ -817,7 +817,7 @@ impl<'tcx> VerifyBound<'tcx> { pub fn cannot_hold(&self) -> bool { match self { - VerifyBound::IfEqBound(..) => false, + VerifyBound::IfEq(..) => false, VerifyBound::IsEmpty => false, VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), From c9bf88ccf92237d17c1be2b353664d8ac73cd971 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:38:24 -0400 Subject: [PATCH 12/18] coallesce docs --- .../src/infer/region_constraints/mod.rs | 97 ++++++------------- 1 file changed, 32 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index f93c9a31be675..19f83e3377a67 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -190,44 +190,7 @@ pub enum GenericKind<'tcx> { /// This is described with an `AnyRegion('a, 'b)` node. #[derive(Debug, Clone)] pub enum VerifyBound<'tcx> { - /// This is a "conditional bound" that checks the result of inference - /// and supplies a bound if it ended up being relevant. It's used in situations - /// like this: - /// - /// ```rust - /// fn foo<'a, 'b, T: SomeTrait<'a>> - /// where - /// >::Item: 'b - /// ``` - /// - /// If we have an obligation like `>::Item: 'c`, then - /// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds - /// up being equal to `'a`, then the where-clauses on function applies, and - /// in that case we can show `'b: 'c`. But if `'?x` winds up being something - /// else, the bound isn't relevant. - /// - /// More abstractly, this function takes a `Binder`. The binder - /// represents an existential binder -- i.e., if you have something like - /// - /// ```rust - /// where for<'a> ::Item: 'a - /// ``` - /// - /// then the `for<'a>` corresponds to the binder. The idea is that we have - /// to find some instantiation of `'a` that can make `>::Item` - /// equal to the final value of `G`, the generic we are checking. - /// - /// ```ignore (pseudo-rust) - /// fn(min) -> bool { - /// exists<'a> { - /// if G == K { - /// B(min) - /// } else { - /// false - /// } - /// } - /// } - /// ``` + /// See [`VerifyIfEq`] docs IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>), /// Given a region `R`, expands to the function: @@ -271,40 +234,44 @@ pub enum VerifyBound<'tcx> { AllBounds(Vec>), } -/// Given a kind K and a bound B, expands to a function like the -/// following, where `G` is the generic for which this verify -/// bound was created: +/// This is a "conditional bound" that checks the result of inference +/// and supplies a bound if it ended up being relevant. It's used in situations +/// like this: /// -/// ```ignore (pseudo-rust) -/// fn(min) -> bool { -/// if G == K { -/// B(min) -/// } else { -/// false -/// } -/// } +/// ```rust +/// fn foo<'a, 'b, T: SomeTrait<'a>> +/// where +/// >::Item: 'b /// ``` /// -/// In other words, if the generic `G` that we are checking is -/// equal to `K`, then check the associated verify bound -/// (otherwise, false). -/// -/// This is used when we have something in the environment that -/// may or may not be relevant, depending on the region inference -/// results. For example, we may have `where >::Item: 'b` in our where-clauses. If we are -/// generating the verify-bound for `>::Item`, then -/// this where-clause is only relevant if `'0` winds up inferred -/// to `'a`. +/// If we have an obligation like `>::Item: 'c`, then +/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds +/// up being equal to `'a`, then the where-clauses on function applies, and +/// in that case we can show `'b: 'c`. But if `'?x` winds up being something +/// else, the bound isn't relevant. /// -/// So we would compile to a verify-bound like +/// In the [`VerifyBound`], this struct is enclosed in `Binder to account +/// for cases like /// -/// ```ignore (illustrative) -/// IfEq(>::Item, AnyRegion('a)) +/// ```rust +/// where for<'a> ::Item: 'a /// ``` /// -/// meaning, if the subject G is equal to `>::Item` -/// (after inference), and `'a: min`, then `G: min`. +/// The idea is that we have to find some instantiation of `'a` that can +/// make `>::Item` equal to the final value of `G`, +/// the generic we are checking. +/// +/// ```ignore (pseudo-rust) +/// fn(min) -> bool { +/// exists<'a> { +/// if G == K { +/// B(min) +/// } else { +/// false +/// } +/// } +/// } +/// ``` #[derive(Debug, Copy, Clone, TypeFoldable)] pub struct VerifyIfEq<'tcx> { /// Type which must match the generic `G` From e6b630c5b178cb19b2453cac9d7b342d6d676def Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:40:06 -0400 Subject: [PATCH 13/18] apply suggestions from oli-obk Co-authored-by: Oli Scherer --- compiler/rustc_borrowck/src/region_infer/mod.rs | 2 +- compiler/rustc_infer/src/infer/outlives/obligations.rs | 4 ++-- .../rustc_infer/src/infer/outlives/test_type_match.rs | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index d45cdc0c990f7..33cf47c00e5f6 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1364,7 +1364,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc); debug!( - "eval_outlives: returning {} because of comparison between points in sup/sub", + "returning {} because of comparison between points in sup/sub", result ); result diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 42a173bf092b7..a268493b28f63 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -318,7 +318,7 @@ where self.delegate.push_verify(origin, generic, region, verify_bound); } - #[tracing::instrument(level = "Debug", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] fn projection_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, @@ -361,7 +361,7 @@ where // }` in the trait definition. approx_env_bounds.retain(|bound_outlives| { // OK to skip binder because we only manipulate and compare against other - // values from the same inder. e.g. if we have (e.g.) `for<'a> >::Item: 'a` + // values from the same binder. e.g. if we have (e.g.) `for<'a> >::Item: 'a` // in `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region. // If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait` // will be invoked with `['b => ^1]` and so we will get `^1` returned. diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index efb889cdefbd3..99fd41976a31f 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -34,7 +34,7 @@ use crate::infer::region_constraints::VerifyIfEq; /// like are used. This is a particular challenge since this function is invoked /// very late in inference and hence cannot make use of the normal inference /// machinery. -#[tracing::instrument(level = "Debug", skip(tcx, param_env))] +#[tracing::instrument(level = "debug", skip(tcx, param_env))] pub fn extract_verify_if_eq<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -63,7 +63,7 @@ pub fn extract_verify_if_eq<'tcx>( } /// True if a (potentially higher-ranked) outlives -#[tracing::instrument(level = "Debug", skip(tcx, param_env))] +#[tracing::instrument(level = "debug", skip(tcx, param_env))] pub(super) fn can_match_erased_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -102,7 +102,7 @@ impl<'tcx> Match<'tcx> { /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern /// is already bound to a different value. - #[tracing::instrument(level = "Debug", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] fn bind( &mut self, br: ty::BoundRegion, @@ -167,7 +167,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { #[instrument(skip(self), level = "debug")] fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { if pattern == value { - return Ok(pattern); + Ok(pattern) } else { relate::super_relate_tys(self, pattern, value) } @@ -181,7 +181,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug!("{}.consts({:?}, {:?})", self.tag(), pattern, value); if pattern == value { - return Ok(pattern); + Ok(pattern) } else { relate::super_relate_consts(self, pattern, value) } From c3137d9e8c6cf28565e533f669e4cf4a1a3378ee Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:53:44 -0400 Subject: [PATCH 14/18] normalize if-eq bounds before testing Hat-tip: aliemjay --- .../src/infer/lexical_region_resolve/mod.rs | 3 +- src/test/ui/nll/vimwiki-core-regression.rs | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/nll/vimwiki-core-regression.rs diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 9fa374a53fcae..455de47acef1b 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -823,10 +823,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ) -> bool { match bound { VerifyBound::IfEq(verify_if_eq_b) => { + let verify_if_eq_b = var_values.normalize(self.region_rels.tcx, *verify_if_eq_b); match test_type_match::extract_verify_if_eq( self.tcx(), self.param_env, - verify_if_eq_b, + &verify_if_eq_b, generic_ty, ) { Some(r) => { diff --git a/src/test/ui/nll/vimwiki-core-regression.rs b/src/test/ui/nll/vimwiki-core-regression.rs new file mode 100644 index 0000000000000..0a4ed7e0a4021 --- /dev/null +++ b/src/test/ui/nll/vimwiki-core-regression.rs @@ -0,0 +1,37 @@ +// check-pass +// +// Regression test from crater run for +// . + + +pub trait ElementLike {} + +pub struct Located where T: ElementLike { + inner: T, +} + +pub struct BlockElement<'a>(&'a str); + +impl ElementLike for BlockElement<'_> {} + + +pub struct Page<'a> { + /// Comprised of the elements within a page + pub elements: Vec>>, +} + +impl<'a, __IdxT> std::ops::Index<__IdxT> for Page<'a> where + Vec>>: std::ops::Index<__IdxT> +{ + type Output = + >> as + std::ops::Index<__IdxT>>::Output; + + #[inline] + fn index(&self, idx: __IdxT) -> &Self::Output { + >> as + std::ops::Index<__IdxT>>::index(&self.elements, idx) + } +} + +fn main() {} From d8337ee2f4b2de4688019bfa6e2074db85cf5565 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:54:55 -0400 Subject: [PATCH 15/18] try to clarify confusing comment --- .../rustc_infer/src/infer/outlives/test_type_match.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 99fd41976a31f..645c2df341ba0 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -57,7 +57,15 @@ pub fn extract_verify_if_eq<'tcx>( } } } else { - // The region does not contain any inference variables. + // The region does not contain any bound variables, so we don't need + // to do any substitution. + // + // Example: + // + // for<'a> >::Item: 'b + // + // In this case, we've now matched and found a value for + // `'a`, but it doesn't affect the bound `'b`. Some(verify_if_eq.bound) } } From 9118fafd40a69fc391c79924acbe08f05b091921 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 16:56:43 -0400 Subject: [PATCH 16/18] remove misleading comment per aliemjay's suggestion --- compiler/rustc_infer/src/infer/outlives/verify.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index ce3da267b00f5..191f5f18ec2a6 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -250,10 +250,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // To start, collect bounds from user environment. Note that // parameter environments are already elaborated, so we don't - // have to worry about that. Comparing using `==` is a bit - // dubious for projections, but it will work for simple cases - // like `T` and `T::Item`. It may not work as well for things - // like `>::Item`. + // have to worry about that. let c_b = self.param_env.caller_bounds(); let param_bounds = self.collect_outlives_from_predicate_list(erased_ty, c_b.into_iter()); From e93e10516ecd75956d4efa030d14b198fac744ec Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 17:00:46 -0400 Subject: [PATCH 17/18] run `x.py fmt` --- compiler/rustc_borrowck/src/region_infer/mod.rs | 5 +---- compiler/rustc_infer/src/infer/outlives/test_type_match.rs | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 33cf47c00e5f6..ec8ba5a7a81be 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1363,10 +1363,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc); - debug!( - "returning {} because of comparison between points in sup/sub", - result - ); + debug!("returning {} because of comparison between points in sup/sub", result); result } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 645c2df341ba0..9f71ebae99e5b 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -174,11 +174,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { #[instrument(skip(self), level = "debug")] fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - if pattern == value { - Ok(pattern) - } else { - relate::super_relate_tys(self, pattern, value) - } + if pattern == value { Ok(pattern) } else { relate::super_relate_tys(self, pattern, value) } } #[instrument(skip(self), level = "debug")] From e7ed8fe481fe103383f55a015a19f641450b76a3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 Jun 2022 17:16:47 -0400 Subject: [PATCH 18/18] add regression tests found in crater --- .../collectivity-regression.rs | 24 +++++++++++++++++++ .../collectivity-regression.stderr | 14 +++++++++++ .../generic-associated-types/issue-92096.rs | 2 +- src/test/ui/nll/snocat-regression.rs | 16 +++++++++++++ src/test/ui/nll/snocat-regression.stderr | 14 +++++++++++ 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/generic-associated-types/collectivity-regression.rs create mode 100644 src/test/ui/generic-associated-types/collectivity-regression.stderr create mode 100644 src/test/ui/nll/snocat-regression.rs create mode 100644 src/test/ui/nll/snocat-regression.stderr diff --git a/src/test/ui/generic-associated-types/collectivity-regression.rs b/src/test/ui/generic-associated-types/collectivity-regression.rs new file mode 100644 index 0000000000000..fb73684390737 --- /dev/null +++ b/src/test/ui/generic-associated-types/collectivity-regression.rs @@ -0,0 +1,24 @@ +// Regression test from https://github.com/rust-lang/rust/pull/98109 + +#![feature(generic_associated_types)] + +pub trait Get { + type Value<'a> + where + Self: 'a; +} + +fn multiply_at(x: T) +where + for<'a> T: Get = ()>, +{ + || { + //~^ `T` does not live long enough + // + // FIXME(#98437). This regressed at some point and + // probably should work. + let _x = x; + }; +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/collectivity-regression.stderr b/src/test/ui/generic-associated-types/collectivity-regression.stderr new file mode 100644 index 0000000000000..a858dd7fddcd2 --- /dev/null +++ b/src/test/ui/generic-associated-types/collectivity-regression.stderr @@ -0,0 +1,14 @@ +error: `T` does not live long enough + --> $DIR/collectivity-regression.rs:15:5 + | +LL | / || { +LL | | +LL | | // +LL | | // FIXME(#98437). This regressed at some point and +LL | | // probably should work. +LL | | let _x = x; +LL | | }; + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/generic-associated-types/issue-92096.rs b/src/test/ui/generic-associated-types/issue-92096.rs index 59dd7ea556345..377b8164ad506 100644 --- a/src/test/ui/generic-associated-types/issue-92096.rs +++ b/src/test/ui/generic-associated-types/issue-92096.rs @@ -19,7 +19,7 @@ where async move { c.connect().await } //~^ ERROR `C` does not live long enough // - // FIXME. This is because we infer at some point a value of + // FIXME(#71723). This is because we infer at some point a value of // // impl Future::Connection<'_>> // diff --git a/src/test/ui/nll/snocat-regression.rs b/src/test/ui/nll/snocat-regression.rs new file mode 100644 index 0000000000000..b2e5995aa5bc2 --- /dev/null +++ b/src/test/ui/nll/snocat-regression.rs @@ -0,0 +1,16 @@ +// Regression test from https://github.com/rust-lang/rust/pull/98109 + +pub fn negotiate(link: S) +where + for<'a> &'a S: 'a, +{ + || { + //~^ ERROR `S` does not live long enough + // + // FIXME(#98437). This regressed at some point and + // probably should work. + let _x = link; + }; +} + +fn main() {} diff --git a/src/test/ui/nll/snocat-regression.stderr b/src/test/ui/nll/snocat-regression.stderr new file mode 100644 index 0000000000000..0868984734d7b --- /dev/null +++ b/src/test/ui/nll/snocat-regression.stderr @@ -0,0 +1,14 @@ +error: `S` does not live long enough + --> $DIR/snocat-regression.rs:7:5 + | +LL | / || { +LL | | +LL | | // +LL | | // FIXME(#98437). This regressed at some point and +LL | | // probably should work. +LL | | let _x = link; +LL | | }; + | |_____^ + +error: aborting due to previous error +