Auto merge of #98109 - nikomatsakis:issue-98095, r=jackh726
fix universes in the NLL type tests In the NLL code, we were not accommodating universes in the `type_test` logic. Fixes #98095. r? `@compiler-errors` This breaks some tests, however, so the purpose of this branch is more explanatory and perhaps to do a crater run.
This commit is contained in:
commit
d017d59ed0
@ -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`.
|
||||
|
@ -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<Rc<PoloniusOutput>>,
|
||||
) -> (Option<ClosureRegionRequirements<'tcx>>, 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<ClosureOutlivesRequirement<'tcx>>>,
|
||||
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<ClosureOutlivesRequirement<'tcx>>,
|
||||
@ -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
|
||||
|
@ -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<RegionResolutionError<'tcx>>) {
|
||||
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) => {
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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., `<T as Foo<'a>>::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),
|
||||
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> <T as Trait<'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),
|
||||
_ => 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);
|
||||
}
|
||||
}
|
||||
|
207
compiler/rustc_infer/src/infer/outlives/test_type_match.rs
Normal file
207
compiler/rustc_infer/src/infer/outlives/test_type_match.rs
Normal file
@ -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<ty::Region<'tcx>> {
|
||||
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> <T as Foo<'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<ty::BoundRegion, ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
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<T>(&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<T: Relate<'tcx>>(
|
||||
&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<T>(
|
||||
&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
|
||||
}
|
||||
}
|
@ -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);
|
||||
if let Some(r) = self.implicit_region_bound {
|
||||
param_bounds.push(VerifyBound::OutlivedBy(r));
|
||||
}
|
||||
|
||||
let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect();
|
||||
|
||||
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::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
|
||||
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, 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::OutlivesPredicate<Ty<'tcx>, 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::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, 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 `<T as Foo<fn(&u32, &u32)>>::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::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
|
||||
erased_ty: Ty<'tcx>,
|
||||
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, 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 `<T as Foo<'a>>::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<Item = ty::Predicate<'tcx>>,
|
||||
) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, 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<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, 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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 <T as
|
||||
/// Trait<'a>>::Item: 'b` in our where-clauses. If we are
|
||||
/// generating the verify-bound for `<T as Trait<'0>>::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(<T as Trait<'a>>::Item, AnyRegion('a))
|
||||
/// ```
|
||||
///
|
||||
/// meaning, if the subject G is equal to `<T as Trait<'a>>::Item`
|
||||
/// (after inference), and `'a: min`, then `G: min`.
|
||||
IfEq(Ty<'tcx>, Box<VerifyBound<'tcx>>),
|
||||
/// 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<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
|
||||
/// <T as SomeTrait<'a>>::Item: 'b
|
||||
/// ```
|
||||
///
|
||||
/// If we have an obligation like `<T as SomeTrait<'?x>>::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> <T as SomeTrait<'a>::Item: 'a
|
||||
/// ```
|
||||
///
|
||||
/// The idea is that we have to find some instantiation of `'a` that can
|
||||
/// make `<T as SomeTrait<'a>>::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()),
|
||||
|
@ -1,4 +1,8 @@
|
||||
// Regression test for #71546.
|
||||
//
|
||||
// Made to pass as part of fixing #98095.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
pub fn serialize_as_csv<V>(value: &V) -> Result<String, &str>
|
||||
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::<String>();
|
||||
//~^ ERROR higher-ranked lifetime error
|
||||
let csv_str: String = value.into_iter().map(|elem| elem.to_string()).collect::<String>();
|
||||
Ok(csv_str)
|
||||
}
|
||||
|
||||
|
@ -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::<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: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::<String>();
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
@ -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<T>(x: T)
|
||||
where
|
||||
for<'a> T: Get<Value<'a> = ()>,
|
||||
{
|
||||
|| {
|
||||
//~^ `T` does not live long enough
|
||||
//
|
||||
// FIXME(#98437). This regressed at some point and
|
||||
// probably should work.
|
||||
let _x = x;
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -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
|
||||
|
@ -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<T> //~ ERROR: the parameter type `T` may not live long enough
|
||||
pub trait IceIce<T>
|
||||
where
|
||||
for<'a> T: 'a,
|
||||
{
|
||||
type Ice<'v>: IntoIterator<Item = &'v T>;
|
||||
//~^ ERROR: the parameter type `T` may not live long enough
|
||||
//~| ERROR: the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -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<T>
|
||||
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<Item = &'v T>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...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<Item = &'v T>;
|
||||
| ^^^^^^^^^^^^ - 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`.
|
@ -1,5 +1,3 @@
|
||||
//check-pass
|
||||
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
trait Foo<T> {
|
||||
@ -16,6 +14,22 @@ impl<T> Foo<T> for () {
|
||||
|
||||
fn foo<T>() {
|
||||
let _: for<'a> fn(<() as Foo<T>>::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<T>>::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<T>>::Type<'a>" is valid".
|
||||
}
|
||||
|
||||
pub fn main() {}
|
||||
|
50
src/test/ui/generic-associated-types/issue-91139.stderr
Normal file
50
src/test/ui/generic-associated-types/issue-91139.stderr
Normal file
@ -0,0 +1,50 @@
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:12
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:12
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:12
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:12
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:58
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:58
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:58
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-91139.rs:16:58
|
||||
|
|
||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
@ -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<Output = <C as Client>::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() {}
|
||||
|
8
src/test/ui/generic-associated-types/issue-92096.stderr
Normal file
8
src/test/ui/generic-associated-types/issue-92096.stderr
Normal file
@ -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
|
||||
|
@ -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,
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
16
src/test/ui/nll/snocat-regression.rs
Normal file
16
src/test/ui/nll/snocat-regression.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Regression test from https://github.com/rust-lang/rust/pull/98109
|
||||
|
||||
pub fn negotiate<S>(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() {}
|
14
src/test/ui/nll/snocat-regression.stderr
Normal file
14
src/test/ui/nll/snocat-regression.stderr
Normal file
@ -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
|
||||
|
21
src/test/ui/nll/type-test-universe.rs
Normal file
21
src/test/ui/nll/type-test-universe.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// Regression test for #98095: make sure that
|
||||
// we detect that S needs to outlive 'static.
|
||||
|
||||
fn outlives_forall<T>()
|
||||
where
|
||||
for<'u> T: 'u,
|
||||
{
|
||||
}
|
||||
|
||||
fn test1<S>() {
|
||||
outlives_forall::<S>();
|
||||
//~^ ERROR `S` does not live long enough
|
||||
}
|
||||
|
||||
struct Value<'a>(&'a ());
|
||||
fn test2<'a>() {
|
||||
outlives_forall::<Value<'a>>();
|
||||
//~^ ERROR lifetime may not live long enough
|
||||
}
|
||||
|
||||
fn main() {}
|
16
src/test/ui/nll/type-test-universe.stderr
Normal file
16
src/test/ui/nll/type-test-universe.stderr
Normal file
@ -0,0 +1,16 @@
|
||||
error: `S` does not live long enough
|
||||
--> $DIR/type-test-universe.rs:11:5
|
||||
|
|
||||
LL | outlives_forall::<S>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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::<Value<'a>>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
37
src/test/ui/nll/vimwiki-core-regression.rs
Normal file
37
src/test/ui/nll/vimwiki-core-regression.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// check-pass
|
||||
//
|
||||
// Regression test from crater run for
|
||||
// <https://github.com/rust-lang/rust/pull/98109>.
|
||||
|
||||
|
||||
pub trait ElementLike {}
|
||||
|
||||
pub struct Located<T> 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<Located<BlockElement<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, __IdxT> std::ops::Index<__IdxT> for Page<'a> where
|
||||
Vec<Located<BlockElement<'a>>>: std::ops::Index<__IdxT>
|
||||
{
|
||||
type Output =
|
||||
<Vec<Located<BlockElement<'a>>> as
|
||||
std::ops::Index<__IdxT>>::Output;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, idx: __IdxT) -> &Self::Output {
|
||||
<Vec<Located<BlockElement<'a>>> as
|
||||
std::ops::Index<__IdxT>>::index(&self.elements, idx)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
18
src/test/ui/regions/forall-wf-ref-reflexive.rs
Normal file
18
src/test/ui/regions/forall-wf-ref-reflexive.rs
Normal file
@ -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<T>()
|
||||
where
|
||||
for<'a> &'a T: 'a,
|
||||
{
|
||||
self_wf2::<T>();
|
||||
//~^ ERROR `T` does not live long enough
|
||||
//
|
||||
// FIXME. This ought to be accepted, presumably.
|
||||
}
|
||||
|
||||
fn main() {}
|
8
src/test/ui/regions/forall-wf-ref-reflexive.stderr
Normal file
8
src/test/ui/regions/forall-wf-ref-reflexive.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/forall-wf-ref-reflexive.rs:12:5
|
||||
|
|
||||
LL | self_wf2::<T>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
15
src/test/ui/regions/forall-wf-reflexive.rs
Normal file
15
src/test/ui/regions/forall-wf-reflexive.rs
Normal file
@ -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<T>()
|
||||
where
|
||||
for<'a> T: 'a,
|
||||
{
|
||||
self_wf1::<T>();
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user