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 fcb0978111ecb..0fe3b45bc7c4c 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, @@ -46,6 +47,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 +561,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 +577,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 +832,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 +849,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 +862,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 +919,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 +951,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 +1181,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,8 +1191,8 @@ 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(verify_if_eq_b) => { + self.eval_if_eq(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b) } VerifyBound::IsEmpty => { @@ -1185,30 +1206,50 @@ 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>, + param_env: ty::ParamEnv<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, - test_ty: Ty<'tcx>, - verify_bound: &VerifyBound<'tcx>, + verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'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); - if generic_ty_normalized == test_ty_normalized { - self.eval_verify_bound(tcx, body, generic_ty, lower_bound, verify_bound) - } else { - false + 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( + 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, } } @@ -1278,6 +1319,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 @@ -1292,6 +1345,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; } @@ -1300,10 +1356,15 @@ 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!("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/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 7975b946ee5ba..455de47acef1b 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>, @@ -818,9 +822,20 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { min: ty::Region<'tcx>, ) -> bool { match bound { - VerifyBound::IfEq(k, b) => { - (var_values.normalize(self.region_rels.tcx, *k) == generic_ty) - && self.bound_is_met(b, var_values, generic_ty, min) + 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, + generic_ty, + ) { + Some(r) => { + self.bound_is_met(&VerifyBound::OutlivedBy(r), var_values, generic_ty, min) + } + + None => false, + } } VerifyBound::OutlivedBy(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/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 2aa535da0e54c..a268493b28f63 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 @@ -363,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 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. + 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 @@ -420,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); @@ -437,6 +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); 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 new file mode 100644 index 0000000000000..9f71ebae99e5b --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -0,0 +1,207 @@ +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. +#[tracing::instrument(level = "debug", skip(tcx, param_env))] +pub fn extract_verify_if_eq<'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 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) + } +} + +/// 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>, + 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. + #[tracing::instrument(level = "debug", skip(self))] + 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>> { + debug!("self.pattern_depth = {:?}", self.pattern_depth); + if let ty::RegionKind::ReLateBound(depth, br) = pattern.kind() && depth == self.pattern_depth { + self.bind(br, value) + } else if pattern == value { + Ok(pattern) + } else { + self.no_match() + } + } + + #[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) } + } + + #[instrument(skip(self), level = "debug")] + 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 { + 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/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 1c521c90686d6..191f5f18ec2a6 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,27 +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(GenericKind::Param(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) } } @@ -122,17 +135,10 @@ 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_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 @@ -159,15 +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)| { - let vb = VerifyBound::OutlivedBy(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). - vb + VerifyBound::OutlivedBy(r) } else { - VerifyBound::IfEq(ty, Box::new(vb)) + let verify_if_eq_b = binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }); + VerifyBound::IfEq(verify_if_eq_b) } }); @@ -219,26 +225,34 @@ 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>, - ) -> Vec, ty::Region<'tcx>>> { - let generic_ty = generic.to_ty(self.tcx); - self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty) + param_ty: ty::ParamTy, + ) -> Vec, ty::Region<'tcx>>>> { + let generic_ty = param_ty.to_ty(self.tcx); + 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, - ) -> Vec, ty::Region<'tcx>>> { + erased_ty: Ty<'tcx>, + ) -> Vec, ty::Region<'tcx>>>> { let tcx = self.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(&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 @@ -253,18 +267,20 @@ 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::Binder::dummy(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 ) }) @@ -344,12 +360,19 @@ 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>>> { - predicates - .filter_map(|p| p.to_opt_type_outlives()) - .filter_map(|p| p.no_bound_vars()) - .filter(move |p| compare_ty(p.0)) + ) -> impl Iterator, ty::Region<'tcx>>>> + { + let tcx = self.tcx; + 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/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index efe254387dcc9..19f83e3377a67 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -190,41 +190,8 @@ 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: - /// - /// ```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`. - IfEq(Ty<'tcx>, Box>), + /// See [`VerifyIfEq`] docs + IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>), /// Given a region `R`, expands to the function: /// @@ -267,6 +234,53 @@ pub enum VerifyBound<'tcx> { AllBounds(Vec>), } +/// 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. +/// +/// In the [`VerifyBound`], this struct is enclosed in `Binder to account +/// for cases like +/// +/// ```rust +/// where for<'a> ::Item: 'a +/// ``` +/// +/// 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` + 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>, @@ -770,7 +784,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()), 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/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-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/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..377b8164ad506 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(#71723). 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 + 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 - 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 + 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 + 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() {} 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() {}