Auto merge of #143545 - compiler-errors:coroutine-obl, r=oli-obk
`-Zhigher-ranked-assumptions`: Consider WF of coroutine witness when proving outlives assumptions ### TL;DR This PR introduces an unstable flag `-Zhigher-ranked-assumptions` which tests out a new algorithm for dealing with some of the higher-ranked outlives problems that come from auto trait bounds on coroutines. See: * rust-lang/rust#110338 While it doesn't fix all of the issues, it certainly fixed many of them, so I'd like to get this landed so people can test the flag on their own code. ### Background Consider, for example: ```rust use std::future::Future; trait Client { type Connecting<'a>: Future + Send where Self: 'a; fn connect(&self) -> Self::Connecting<'_>; } fn call_connect<C>(c: C) -> impl Future + Send where C: Client + Send + Sync, { async move { c.connect().await } } ``` Due to the fact that we erase the lifetimes in a coroutine, we can think of the interior type of the async block as something like: `exists<'r, 's> { C, &'r C, C::Connecting<'s> }`. The first field is the `c` we capture, the second is the auto-ref that we perform on the call to `.connect()`, and the third is the resulting future we're awaiting at the first and only await point. Note that every region is uniquified differently in the interior types. For the async block to be `Send`, we must prove that both of the interior types are `Send`. First, we have an `exists<'r, 's>` binder, which needs to be instantiated universally since we treat the regions in this binder as *unknown*[^exist]. This gives us two types: `{ &'!r C, C::Connecting<'!s> }`. Proving `&'!r C: Send` is easy due to a [`Send`](https://doc.rust-lang.org/nightly/std/marker/trait.Send.html#impl-Send-for-%26T) impl for references. Proving `C::Connecting<'!s>: Send` can only be done via the item bound, which then requires `C: '!s` to hold (due to the `where Self: 'a` on the associated type definition). Unfortunately, we don't know that `C: '!s` since we stripped away any relationship between the interior type and the param `C`. This leads to a bogus borrow checker error today! ### Approach Coroutine interiors are well-formed by virtue of them being borrow-checked, as long as their callers are invoking their parent functions in a well-formed way, then substitutions should also be well-formed. Therefore, in our example above, we should be able to deduce the assumption that `C: '!s` holds from the well-formedness of the interior type `C::Connecting<'!s>`. This PR introduces the notion of *coroutine assumptions*, which are the outlives assumptions that we can assume hold due to the well-formedness of a coroutine's interior types. These are computed alongside the coroutine types in the `CoroutineWitnessTypes` struct. When we instantiate the binder when proving an auto trait for a coroutine, we instantiate the `CoroutineWitnessTypes` and stash these newly instantiated assumptions in the region storage in the `InferCtxt`. Later on in lexical region resolution or MIR borrowck, we use these registered assumptions to discharge any placeholder outlives obligations that we would otherwise not be able to prove. ### How well does it work? I've added a ton of tests of different reported situations that users have shared on issues like rust-lang/rust#110338, and an (anecdotally) large number of those examples end up working straight out of the box! Some limitations are described below. ### How badly does it not work? The behavior today is quite rudimentary, since we currently discharge the placeholder assumptions pretty early in region resolution. This manifests itself as some limitations on the code that we accept. For example, `tests/ui/async-await/higher-ranked-auto-trait-11.rs` continues to fail. In that test, we must prove that a placeholder is equal to a universal for a param-env candidate to hold when proving an auto trait, e.g. `'!1 = 'a` is required to prove `T: Trait<'!1>` in a param-env that has `T: Trait<'a>`. Unfortunately, at that point in the MIR body, we only know that the placeholder is equal to some body-local existential NLL var `'?2`, which only gets equated to the universal `'a` when being stored into the return local later on in MIR borrowck. This could be fixed by integrating these assumptions into the type outlives machinery in a more first-class way, and delaying things to the end of MIR typeck when we know the full relationship between existential and universal NLL vars. Doing this integration today is quite difficult today. `tests/ui/async-await/higher-ranked-auto-trait-11.rs` fails because we don't compute the full transitive outlives relations between placeholders. In that test, we have in our region assumptions that some `'!1 = '!2` and `'!2 = '!3`, but we must prove `'!1 = '!3`. This can be fixed by computing the set of coroutine outlives assumptions in a more transitive way, or as I mentioned above, integrating these assumptions into the type outlives machinery in a more first-class way, since it's already responsible for the transitive outlives assumptions of universals. ### Moving forward I'm still quite happy with this implementation, and I'd like to land it for testing. I may work on overhauling both the way we compute these coroutine assumptions and also how we deal with the assumptions during (lexical/nll) region checking. But for now, I'd like to give users a chance to try out this new `-Zhigher-ranked-assumptions` flag to uncover more shortcomings. [^exist]: Instantiating this binder with infer regions would be incomplete, since we'd be asking for *some* instantiation of the interior types, not proving something for *all* instantiations of the interior types.
This commit is contained in:
commit
1aa5b22465
77 changed files with 1645 additions and 159 deletions
|
|
@ -1,3 +1,4 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_infer::infer::canonical::QueryRegionConstraints;
|
||||
use rustc_infer::infer::outlives::env::RegionBoundPairs;
|
||||
|
|
@ -7,7 +8,7 @@ use rustc_infer::infer::{InferCtxt, SubregionOrigin};
|
|||
use rustc_infer::traits::query::type_op::DeeplyNormalize;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
|
||||
self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
|
||||
|
|
@ -70,10 +71,12 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
|
||||
let QueryRegionConstraints { outlives } = query_constraints;
|
||||
let QueryRegionConstraints { outlives, assumptions } = query_constraints;
|
||||
let assumptions =
|
||||
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
|
||||
|
||||
for &(predicate, constraint_category) in outlives {
|
||||
self.convert(predicate, constraint_category);
|
||||
self.convert(predicate, constraint_category, &assumptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,15 +115,20 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
|
||||
self.category = outlives_requirement.category;
|
||||
self.span = outlives_requirement.blame_span;
|
||||
self.convert(ty::OutlivesPredicate(subject, outlived_region), self.category);
|
||||
self.convert(
|
||||
ty::OutlivesPredicate(subject, outlived_region),
|
||||
self.category,
|
||||
&Default::default(),
|
||||
);
|
||||
}
|
||||
(self.category, self.span, self.from_closure) = backup;
|
||||
}
|
||||
|
||||
fn convert(
|
||||
&mut self,
|
||||
predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
|
||||
predicate: ty::ArgOutlivesPredicate<'tcx>,
|
||||
constraint_category: ConstraintCategory<'tcx>,
|
||||
higher_ranked_assumptions: &FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
debug!("generate: constraints at: {:#?}", self.locations);
|
||||
|
|
@ -150,7 +158,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let mut next_outlives_predicates = vec![];
|
||||
for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
|
||||
for (pred, constraint_category) in outlives_predicates {
|
||||
// Constraint is implied by a coroutine's well-formedness.
|
||||
if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
|
||||
&& higher_ranked_assumptions.contains(&pred)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let ty::OutlivesPredicate(k1, r2) = pred;
|
||||
match k1.kind() {
|
||||
GenericArgKind::Lifetime(r1) => {
|
||||
let r1_vid = self.to_region_vid(r1);
|
||||
|
|
@ -266,14 +282,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
next_outlives_predicates: &mut Vec<(
|
||||
ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
|
||||
ty::ArgOutlivesPredicate<'tcx>,
|
||||
ConstraintCategory<'tcx>,
|
||||
)>,
|
||||
) -> Ty<'tcx> {
|
||||
match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
|
||||
{
|
||||
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
|
||||
if let Some(QueryRegionConstraints { outlives }) = constraints {
|
||||
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
|
||||
if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
|
||||
next_outlives_predicates.extend(outlives.iter().copied());
|
||||
}
|
||||
ty
|
||||
|
|
|
|||
|
|
@ -131,6 +131,11 @@ pub(crate) fn type_check<'tcx>(
|
|||
pre_obligations.is_empty(),
|
||||
"there should be no incoming region obligations = {pre_obligations:#?}",
|
||||
);
|
||||
let pre_assumptions = infcx.take_registered_region_assumptions();
|
||||
assert!(
|
||||
pre_assumptions.is_empty(),
|
||||
"there should be no incoming region assumptions = {pre_assumptions:#?}",
|
||||
);
|
||||
|
||||
debug!(?normalized_inputs_and_output);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ use smallvec::smallvec;
|
|||
|
||||
/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
|
||||
/// must be added to the struct header.
|
||||
pub(crate) type RequiredPredicates<'tcx> =
|
||||
FxIndexMap<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, Span>;
|
||||
pub(crate) type RequiredPredicates<'tcx> = FxIndexMap<ty::ArgOutlivesPredicate<'tcx>, Span>;
|
||||
|
||||
/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
|
||||
/// outlives_component and add it to `required_predicates`
|
||||
|
|
|
|||
|
|
@ -116,9 +116,15 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
|
||||
let region_obligations = self.take_registered_region_obligations();
|
||||
let region_assumptions = self.take_registered_region_assumptions();
|
||||
debug!(?region_obligations);
|
||||
let region_constraints = self.with_region_constraints(|region_constraints| {
|
||||
make_query_region_constraints(tcx, region_obligations, region_constraints)
|
||||
make_query_region_constraints(
|
||||
tcx,
|
||||
region_obligations,
|
||||
region_constraints,
|
||||
region_assumptions,
|
||||
)
|
||||
});
|
||||
debug!(?region_constraints);
|
||||
|
||||
|
|
@ -169,6 +175,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.register_outlives_constraint(predicate, cause);
|
||||
}
|
||||
|
||||
for assumption in &query_response.value.region_constraints.assumptions {
|
||||
let assumption = instantiate_value(self.tcx, &result_args, *assumption);
|
||||
self.register_region_assumption(assumption);
|
||||
}
|
||||
|
||||
let user_result: R =
|
||||
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
|
||||
|
||||
|
|
@ -292,6 +303,18 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}),
|
||||
);
|
||||
|
||||
// FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions
|
||||
// at once, rather than calling `instantiate_value` repeatedly which may
|
||||
// create more universes.
|
||||
output_query_region_constraints.assumptions.extend(
|
||||
query_response
|
||||
.value
|
||||
.region_constraints
|
||||
.assumptions
|
||||
.iter()
|
||||
.map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)),
|
||||
);
|
||||
|
||||
let user_result: R =
|
||||
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
|
||||
|
||||
|
|
@ -567,6 +590,7 @@ pub fn make_query_region_constraints<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
outlives_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
|
||||
region_constraints: &RegionConstraintData<'tcx>,
|
||||
assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
) -> QueryRegionConstraints<'tcx> {
|
||||
let RegionConstraintData { constraints, verifys } = region_constraints;
|
||||
|
||||
|
|
@ -602,5 +626,5 @@ pub fn make_query_region_constraints<'tcx>(
|
|||
}))
|
||||
.collect();
|
||||
|
||||
QueryRegionConstraints { outlives }
|
||||
QueryRegionConstraints { outlives, assumptions }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> {
|
|||
/// that all type inference variables have been bound and so forth.
|
||||
region_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
|
||||
|
||||
/// The outlives bounds that we assume must hold about placeholders that
|
||||
/// come from instantiating the binder of coroutine-witnesses. These bounds
|
||||
/// are deduced from the well-formedness of the witness's types, and are
|
||||
/// necessary because of the way we anonymize the regions in a coroutine,
|
||||
/// which may cause types to no longer be considered well-formed.
|
||||
region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
|
||||
/// Caches for opaque type inference.
|
||||
opaque_type_storage: OpaqueTypeStorage<'tcx>,
|
||||
}
|
||||
|
|
@ -164,7 +171,8 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
|||
int_unification_storage: Default::default(),
|
||||
float_unification_storage: Default::default(),
|
||||
region_constraint_storage: Some(Default::default()),
|
||||
region_obligations: vec![],
|
||||
region_obligations: Default::default(),
|
||||
region_assumptions: Default::default(),
|
||||
opaque_type_storage: Default::default(),
|
||||
}
|
||||
}
|
||||
|
|
@ -174,6 +182,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
|||
&self.region_obligations
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn region_assumptions(&self) -> &[ty::ArgOutlivesPredicate<'tcx>] {
|
||||
&self.region_assumptions
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> {
|
||||
self.projection_cache.with_log(&mut self.undo_log)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::transitive_relation::TransitiveRelationBuilder;
|
||||
use rustc_middle::{bug, ty};
|
||||
use tracing::debug;
|
||||
|
|
@ -39,6 +39,9 @@ pub struct OutlivesEnvironment<'tcx> {
|
|||
/// optimized in the future, though.
|
||||
region_bound_pairs: RegionBoundPairs<'tcx>,
|
||||
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
|
||||
/// Assumptions that come from the well-formedness of coroutines that we prove
|
||||
/// auto trait bounds for during the type checking of this body.
|
||||
higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
}
|
||||
|
||||
/// "Region-bound pairs" tracks outlives relations that are known to
|
||||
|
|
@ -52,6 +55,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
|||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
|
||||
extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
|
||||
higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
) -> Self {
|
||||
let mut region_relation = TransitiveRelationBuilder::default();
|
||||
let mut region_bound_pairs = RegionBoundPairs::default();
|
||||
|
|
@ -88,6 +92,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
|||
known_type_outlives,
|
||||
free_region_map: FreeRegionMap { relation: region_relation.freeze() },
|
||||
region_bound_pairs,
|
||||
higher_ranked_assumptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,4 +107,8 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
|||
pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
|
||||
&self.known_type_outlives
|
||||
}
|
||||
|
||||
pub fn higher_ranked_assumptions(&self) -> &FxHashSet<ty::ArgOutlivesPredicate<'tcx>> {
|
||||
&self.higher_ranked_assumptions
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use super::region_constraints::{RegionConstraintData, UndoLog};
|
|||
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
|
||||
use crate::infer::free_regions::RegionRelations;
|
||||
use crate::infer::lexical_region_resolve;
|
||||
use crate::infer::region_constraints::Constraint;
|
||||
|
||||
pub mod env;
|
||||
pub mod for_liveness;
|
||||
|
|
@ -54,18 +55,29 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
let storage = {
|
||||
let mut storage = {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let inner = &mut *inner;
|
||||
assert!(
|
||||
self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(),
|
||||
"region_obligations not empty: {:#?}",
|
||||
inner.region_obligations
|
||||
inner.region_obligations,
|
||||
);
|
||||
assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
|
||||
inner.region_constraint_storage.take().expect("regions already resolved")
|
||||
};
|
||||
|
||||
// Filter out any region-region outlives assumptions that are implied by
|
||||
// coroutine well-formedness.
|
||||
if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions {
|
||||
storage.data.constraints.retain(|(constraint, _)| match *constraint {
|
||||
Constraint::RegSubReg(r1, r2) => !outlives_env
|
||||
.higher_ranked_assumptions()
|
||||
.contains(&ty::OutlivesPredicate(r2.into(), r1)),
|
||||
_ => true,
|
||||
});
|
||||
}
|
||||
|
||||
let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map());
|
||||
|
||||
let (lexical_region_resolutions, errors) =
|
||||
|
|
@ -93,6 +105,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
"region_obligations not empty: {:#?}",
|
||||
self.inner.borrow().region_obligations
|
||||
);
|
||||
assert!(
|
||||
self.inner.borrow().region_assumptions.is_empty(),
|
||||
"region_assumptions not empty: {:#?}",
|
||||
self.inner.borrow().region_assumptions
|
||||
);
|
||||
|
||||
self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ use crate::traits::{ObligationCause, ObligationCauseCode};
|
|||
impl<'tcx> InferCtxt<'tcx> {
|
||||
pub fn register_outlives_constraint(
|
||||
&self,
|
||||
ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
|
||||
ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
) {
|
||||
match arg.kind() {
|
||||
|
|
@ -170,6 +170,16 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
std::mem::take(&mut self.inner.borrow_mut().region_obligations)
|
||||
}
|
||||
|
||||
pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.undo_log.push(UndoLog::PushRegionAssumption);
|
||||
inner.region_assumptions.push(assumption);
|
||||
}
|
||||
|
||||
pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
|
||||
std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
|
||||
}
|
||||
|
||||
/// Process the region obligations that must be proven (during
|
||||
/// `regionck`) for the given `body_id`, given information about
|
||||
/// the region bounds in scope and so forth.
|
||||
|
|
@ -220,6 +230,14 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
let (sup_type, sub_region) =
|
||||
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
|
||||
|
||||
if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
|
||||
&& outlives_env
|
||||
.higher_ranked_assumptions()
|
||||
.contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!(?sup_type, ?sub_region, ?origin);
|
||||
|
||||
let outlives = &mut TypeOutlives::new(
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> {
|
|||
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
|
||||
ProjectionCache(traits::UndoLog<'tcx>),
|
||||
PushTypeOutlivesConstraint,
|
||||
PushRegionAssumption,
|
||||
}
|
||||
|
||||
macro_rules! impl_from {
|
||||
|
|
@ -77,6 +78,9 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
|
|||
let popped = self.region_obligations.pop();
|
||||
assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
|
||||
}
|
||||
UndoLog::PushRegionAssumption => {
|
||||
self.region_assumptions.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> {
|
|||
#[derive(HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct QueryRegionConstraints<'tcx> {
|
||||
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
|
||||
pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
}
|
||||
|
||||
impl QueryRegionConstraints<'_> {
|
||||
/// Represents an empty (trivially true) set of region
|
||||
/// constraints.
|
||||
/// Represents an empty (trivially true) set of region constraints.
|
||||
///
|
||||
/// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions?
|
||||
/// Because I don't expect for us to get cases where an assumption from one query would
|
||||
/// discharge a requirement from another query, which is a potential problem if we did throw
|
||||
/// away these assumptions because there were no constraints.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.outlives.is_empty()
|
||||
self.outlives.is_empty() && self.assumptions.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,8 +135,7 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
|
|||
}
|
||||
}
|
||||
|
||||
pub type QueryOutlivesConstraint<'tcx> =
|
||||
(ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>);
|
||||
pub type QueryOutlivesConstraint<'tcx> = (ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CanonicalParamEnvCache<'tcx> {
|
||||
|
|
|
|||
|
|
@ -988,7 +988,7 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
query coroutine_hidden_types(
|
||||
def_id: DefId
|
||||
def_id: DefId,
|
||||
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
|
||||
desc { "looking up the hidden types stored across await points in a coroutine" }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
type BoundRegion = ty::BoundRegion;
|
||||
type PlaceholderRegion = ty::PlaceholderRegion;
|
||||
|
||||
type RegionAssumptions = &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>;
|
||||
|
||||
type ParamEnv = ty::ParamEnv<'tcx>;
|
||||
type Predicate = Predicate<'tcx>;
|
||||
|
||||
|
|
@ -878,6 +880,7 @@ pub struct CtxtInterners<'tcx> {
|
|||
offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>,
|
||||
valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>,
|
||||
patterns: InternedSet<'tcx, List<ty::Pattern<'tcx>>>,
|
||||
outlives: InternedSet<'tcx, List<ty::ArgOutlivesPredicate<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'tcx> CtxtInterners<'tcx> {
|
||||
|
|
@ -915,6 +918,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
offset_of: InternedSet::with_capacity(N),
|
||||
valtree: InternedSet::with_capacity(N),
|
||||
patterns: InternedSet::with_capacity(N),
|
||||
outlives: InternedSet::with_capacity(N),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2696,6 +2700,7 @@ slice_interners!(
|
|||
captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
|
||||
offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
|
||||
patterns: pub mk_patterns(Pattern<'tcx>),
|
||||
outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>),
|
||||
);
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
|
|
@ -3111,6 +3116,17 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs))
|
||||
}
|
||||
|
||||
pub fn mk_outlives_from_iter<I, T>(self, iter: I) -> T::Output
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: CollectAndApply<
|
||||
ty::ArgOutlivesPredicate<'tcx>,
|
||||
&'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
>,
|
||||
{
|
||||
T::collect_and_apply(iter, |xs| self.mk_outlives(xs))
|
||||
}
|
||||
|
||||
/// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`,
|
||||
/// typically generated by `#[derive(LintDiagnostic)]`).
|
||||
#[track_caller]
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ pub use self::opaque_types::OpaqueTypeKey;
|
|||
pub use self::parameterized::ParameterizedOverTcx;
|
||||
pub use self::pattern::{Pattern, PatternKind};
|
||||
pub use self::predicate::{
|
||||
AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
|
||||
AliasTerm, ArgOutlivesPredicate, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
|
||||
ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef,
|
||||
HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
|
||||
PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub type SubtypePredicate<'tcx> = ir::SubtypePredicate<TyCtxt<'tcx>>;
|
|||
pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate<TyCtxt<'tcx>, T>;
|
||||
pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>;
|
||||
pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>;
|
||||
pub type ArgOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>;
|
||||
pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
|
||||
pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
|
||||
pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
|
||||
|
|
|
|||
|
|
@ -802,4 +802,5 @@ list_fold! {
|
|||
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates,
|
||||
&'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems,
|
||||
&'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns,
|
||||
&'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> : mk_outlives,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2256,6 +2256,8 @@ options! {
|
|||
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
|
||||
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"explicitly enable the `cfg(target_thread_local)` directive"),
|
||||
higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED],
|
||||
"allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"),
|
||||
hint_mostly_unused: bool = (false, parse_bool, [TRACKED],
|
||||
"hint that most of this crate will go unused, to minimize work for uncalled functions"),
|
||||
human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_infer::infer::{InferCtxt, RegionResolutionError};
|
|||
use rustc_macros::extension;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, elaborate};
|
||||
|
||||
use crate::traits::ScrubbedTraitError;
|
||||
use crate::traits::outlives_bounds::InferCtxtExt;
|
||||
|
|
@ -46,6 +46,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(-Znext-trait-solver): Normalize these.
|
||||
let higher_ranked_assumptions = infcx.take_registered_region_assumptions();
|
||||
let higher_ranked_assumptions =
|
||||
elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions);
|
||||
|
||||
// FIXME: This needs to be modified so that we normalize the known type
|
||||
// outlives obligations then elaborate them into their region/type components.
|
||||
// Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
|
||||
|
|
@ -59,6 +64,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
|||
assumed_wf_tys,
|
||||
disable_implied_bounds_hack,
|
||||
),
|
||||
higher_ranked_assumptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,14 +206,18 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||
.map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
|
||||
}
|
||||
|
||||
fn make_deduplicated_outlives_constraints(
|
||||
&self,
|
||||
) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
|
||||
fn make_deduplicated_outlives_constraints(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
|
||||
// Cannot use `take_registered_region_obligations` as we may compute the response
|
||||
// inside of a `probe` whenever we have multiple choices inside of the solver.
|
||||
let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
|
||||
let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
|
||||
let region_constraints = self.0.with_region_constraints(|region_constraints| {
|
||||
make_query_region_constraints(self.tcx, region_obligations, region_constraints)
|
||||
make_query_region_constraints(
|
||||
self.tcx,
|
||||
region_obligations,
|
||||
region_constraints,
|
||||
region_assumptions,
|
||||
)
|
||||
});
|
||||
|
||||
let mut seen = FxHashSet::default();
|
||||
|
|
|
|||
|
|
@ -461,6 +461,7 @@ fn impl_intersection_has_negative_obligation(
|
|||
// requirements, when proving the negated where clauses below.
|
||||
drop(equate_obligations);
|
||||
drop(infcx.take_registered_region_obligations());
|
||||
drop(infcx.take_registered_region_assumptions());
|
||||
drop(infcx.take_and_reset_region_constraints());
|
||||
|
||||
plug_infer_with_placeholders(
|
||||
|
|
|
|||
|
|
@ -78,7 +78,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
|
|||
bounds.retain(|bound| !bound.has_placeholders());
|
||||
|
||||
if !constraints.is_empty() {
|
||||
let QueryRegionConstraints { outlives } = constraints;
|
||||
// FIXME(higher_ranked_auto): Should we register assumptions here?
|
||||
// We otherwise would get spurious errors if normalizing an implied
|
||||
// outlives bound required proving some higher-ranked coroutine obl.
|
||||
let QueryRegionConstraints { outlives, assumptions: _ } = constraints;
|
||||
let cause = ObligationCause::misc(span, body_id);
|
||||
for &(predicate, _) in &outlives {
|
||||
infcx.register_outlives_constraint(predicate, &cause);
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ where
|
|||
pre_obligations.is_empty(),
|
||||
"scrape_region_constraints: incoming region obligations = {pre_obligations:#?}",
|
||||
);
|
||||
let pre_assumptions = infcx.take_registered_region_assumptions();
|
||||
assert!(
|
||||
pre_assumptions.is_empty(),
|
||||
"scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}",
|
||||
);
|
||||
|
||||
let value = infcx.commit_if_ok(|_| {
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
|
|
@ -100,11 +105,13 @@ where
|
|||
let value = infcx.resolve_vars_if_possible(value);
|
||||
|
||||
let region_obligations = infcx.take_registered_region_obligations();
|
||||
let region_assumptions = infcx.take_registered_region_assumptions();
|
||||
let region_constraint_data = infcx.take_and_reset_region_constraints();
|
||||
let region_constraints = query_response::make_query_region_constraints(
|
||||
infcx.tcx,
|
||||
region_obligations,
|
||||
®ion_constraint_data,
|
||||
region_assumptions,
|
||||
);
|
||||
|
||||
if region_constraints.is_empty() {
|
||||
|
|
|
|||
|
|
@ -180,8 +180,9 @@ where
|
|||
span,
|
||||
)?;
|
||||
output.error_info = error_info;
|
||||
if let Some(QueryRegionConstraints { outlives }) = output.constraints {
|
||||
if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints {
|
||||
region_constraints.outlives.extend(outlives.iter().cloned());
|
||||
region_constraints.assumptions.extend(assumptions.iter().cloned());
|
||||
}
|
||||
output.constraints = if region_constraints.is_empty() {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -411,18 +411,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
|
||||
let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty);
|
||||
|
||||
let types = self.constituent_types_for_ty(self_ty)?;
|
||||
let types = self.infcx.enter_forall_and_leak_universe(types);
|
||||
let constituents = self.constituent_types_for_auto_trait(self_ty)?;
|
||||
let constituents = self.infcx.enter_forall_and_leak_universe(constituents);
|
||||
|
||||
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
|
||||
let obligations = self.collect_predicates_for_types(
|
||||
let mut obligations = self.collect_predicates_for_types(
|
||||
obligation.param_env,
|
||||
cause,
|
||||
cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.predicate.def_id(),
|
||||
types,
|
||||
constituents.types,
|
||||
);
|
||||
|
||||
// FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
|
||||
// and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
|
||||
// matter yet.
|
||||
for assumption in constituents.assumptions {
|
||||
let assumption = normalize_with_depth_to(
|
||||
self,
|
||||
obligation.param_env,
|
||||
cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
assumption,
|
||||
&mut obligations,
|
||||
);
|
||||
self.infcx.register_region_assumption(assumption);
|
||||
}
|
||||
|
||||
Ok(obligations)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use rustc_infer::infer::DefineOpaqueTypes;
|
|||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::infer::relate::TypeRelation;
|
||||
use rustc_infer::traits::{PredicateObligations, TraitObligation};
|
||||
use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
|
||||
pub use rustc_middle::traits::select::*;
|
||||
|
|
@ -2256,10 +2257,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
/// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
|
||||
/// ```
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn constituent_types_for_ty(
|
||||
fn constituent_types_for_auto_trait(
|
||||
&self,
|
||||
t: Ty<'tcx>,
|
||||
) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
|
||||
) -> Result<ty::Binder<'tcx, AutoImplConstituents<'tcx>>, SelectionError<'tcx>> {
|
||||
Ok(match *t.kind() {
|
||||
ty::Uint(_)
|
||||
| ty::Int(_)
|
||||
|
|
@ -2270,17 +2271,26 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
| ty::Error(_)
|
||||
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Never
|
||||
| ty::Char => ty::Binder::dummy(Vec::new()),
|
||||
| ty::Char => {
|
||||
ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] })
|
||||
}
|
||||
|
||||
// This branch is only for `experimental_default_bounds`.
|
||||
// Other foreign types were rejected earlier in
|
||||
// `assemble_candidates_from_auto_impls`.
|
||||
ty::Foreign(..) => ty::Binder::dummy(Vec::new()),
|
||||
ty::Foreign(..) => {
|
||||
ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] })
|
||||
}
|
||||
|
||||
ty::UnsafeBinder(ty) => ty.map_bound(|ty| vec![ty]),
|
||||
ty::UnsafeBinder(ty) => {
|
||||
ty.map_bound(|ty| AutoImplConstituents { types: vec![ty], assumptions: vec![] })
|
||||
}
|
||||
|
||||
// Treat this like `struct str([u8]);`
|
||||
ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
|
||||
ty::Str => ty::Binder::dummy(AutoImplConstituents {
|
||||
types: vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)],
|
||||
assumptions: vec![],
|
||||
}),
|
||||
|
||||
ty::Placeholder(..)
|
||||
| ty::Dynamic(..)
|
||||
|
|
@ -2292,30 +2302,41 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
}
|
||||
|
||||
ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
|
||||
ty::Binder::dummy(vec![element_ty])
|
||||
ty::Binder::dummy(AutoImplConstituents {
|
||||
types: vec![element_ty],
|
||||
assumptions: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => ty::Binder::dummy(vec![ty]),
|
||||
ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => {
|
||||
ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => {
|
||||
// (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
|
||||
ty::Binder::dummy(tys.iter().collect())
|
||||
ty::Binder::dummy(AutoImplConstituents {
|
||||
types: tys.iter().collect(),
|
||||
assumptions: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
ty::Closure(_, args) => {
|
||||
let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
|
||||
ty::Binder::dummy(vec![ty])
|
||||
ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
|
||||
ty::Binder::dummy(vec![ty])
|
||||
ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
|
||||
}
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
|
||||
let witness = args.as_coroutine().witness();
|
||||
ty::Binder::dummy([ty].into_iter().chain(iter::once(witness)).collect())
|
||||
ty::Binder::dummy(AutoImplConstituents {
|
||||
types: [ty].into_iter().chain(iter::once(witness)).collect(),
|
||||
assumptions: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
ty::CoroutineWitness(def_id, args) => self
|
||||
|
|
@ -2323,16 +2344,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
.tcx
|
||||
.coroutine_hidden_types(def_id)
|
||||
.instantiate(self.infcx.tcx, args)
|
||||
.map_bound(|witness| witness.types.to_vec()),
|
||||
.map_bound(|witness| AutoImplConstituents {
|
||||
types: witness.types.to_vec(),
|
||||
assumptions: witness.assumptions.to_vec(),
|
||||
}),
|
||||
|
||||
// For `PhantomData<T>`, we pass `T`.
|
||||
ty::Adt(def, args) if def.is_phantom_data() => {
|
||||
ty::Binder::dummy(args.types().collect())
|
||||
ty::Binder::dummy(AutoImplConstituents {
|
||||
types: args.types().collect(),
|
||||
assumptions: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
ty::Adt(def, args) => {
|
||||
ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect())
|
||||
}
|
||||
ty::Adt(def, args) => ty::Binder::dummy(AutoImplConstituents {
|
||||
types: def.all_fields().map(|f| f.ty(self.tcx(), args)).collect(),
|
||||
assumptions: vec![],
|
||||
}),
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
if self.infcx.can_define_opaque_ty(def_id) {
|
||||
|
|
@ -2342,7 +2370,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
// which enforces a DAG between the functions requiring
|
||||
// the auto trait bounds in question.
|
||||
match self.tcx().type_of_opaque(def_id) {
|
||||
Ok(ty) => ty::Binder::dummy(vec![ty.instantiate(self.tcx(), args)]),
|
||||
Ok(ty) => ty::Binder::dummy(AutoImplConstituents {
|
||||
types: vec![ty.instantiate(self.tcx(), args)],
|
||||
assumptions: vec![],
|
||||
}),
|
||||
Err(_) => {
|
||||
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
|
||||
}
|
||||
|
|
@ -3134,3 +3165,9 @@ pub(crate) enum ProjectionMatchesProjection {
|
|||
Ambiguous,
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
|
||||
pub(crate) struct AutoImplConstituents<'tcx> {
|
||||
pub types: Vec<Ty<'tcx>>,
|
||||
pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::{self, TyCtxt, fold_regions};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
|
||||
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions};
|
||||
use rustc_trait_selection::traits::{ObligationCtxt, with_replaced_escaping_bound_vars};
|
||||
|
||||
/// Return the set of types that should be taken into account when checking
|
||||
/// trait bounds on a coroutine's internal state. This properly replaces
|
||||
|
|
@ -30,8 +35,57 @@ pub(crate) fn coroutine_hidden_types<'tcx>(
|
|||
}),
|
||||
);
|
||||
|
||||
let assumptions = compute_assumptions(tcx, def_id, bound_tys);
|
||||
|
||||
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
|
||||
ty::CoroutineWitnessTypes { types: bound_tys },
|
||||
ty::CoroutineWitnessTypes { types: bound_tys, assumptions },
|
||||
tcx.mk_bound_variable_kinds(&vars),
|
||||
))
|
||||
}
|
||||
|
||||
fn compute_assumptions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
bound_tys: &'tcx ty::List<Ty<'tcx>>,
|
||||
) -> &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> {
|
||||
let infcx = tcx.infer_ctxt().build(ty::TypingMode::Analysis {
|
||||
defining_opaque_types_and_generators: ty::List::empty(),
|
||||
});
|
||||
with_replaced_escaping_bound_vars(&infcx, &mut vec![None], bound_tys, |bound_tys| {
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
ocx.register_obligations(bound_tys.iter().map(|ty| {
|
||||
Obligation::new(
|
||||
tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty::ClauseKind::WellFormed(ty.into()),
|
||||
)
|
||||
}));
|
||||
let _errors = ocx.select_all_or_error();
|
||||
|
||||
let region_obligations = infcx.take_registered_region_obligations();
|
||||
let region_assumptions = infcx.take_registered_region_assumptions();
|
||||
let region_constraints = infcx.take_and_reset_region_constraints();
|
||||
|
||||
let outlives = make_query_region_constraints(
|
||||
tcx,
|
||||
region_obligations,
|
||||
®ion_constraints,
|
||||
region_assumptions,
|
||||
)
|
||||
.outlives
|
||||
.fold_with(&mut OpportunisticRegionResolver::new(&infcx));
|
||||
|
||||
tcx.mk_outlives_from_iter(
|
||||
outlives
|
||||
.into_iter()
|
||||
.map(|(o, _)| o)
|
||||
// FIXME(higher_ranked_auto): We probably should deeply resolve these before
|
||||
// filtering out infers which only correspond to unconstrained infer regions
|
||||
// which we can sometimes get.
|
||||
.filter(|o| !o.has_infer()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,3 +371,54 @@ impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I,
|
|||
(0, upper)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elaborate_outlives_assumptions<I: Interner>(
|
||||
cx: I,
|
||||
assumptions: impl IntoIterator<Item = ty::OutlivesPredicate<I, I::GenericArg>>,
|
||||
) -> HashSet<ty::OutlivesPredicate<I, I::GenericArg>> {
|
||||
let mut collected = HashSet::default();
|
||||
|
||||
for ty::OutlivesPredicate(arg1, r2) in assumptions {
|
||||
collected.insert(ty::OutlivesPredicate(arg1, r2));
|
||||
match arg1.kind() {
|
||||
// Elaborate the components of an type, since we may have substituted a
|
||||
// generic coroutine with a more specific type.
|
||||
ty::GenericArgKind::Type(ty1) => {
|
||||
let mut components = smallvec![];
|
||||
push_outlives_components(cx, ty1, &mut components);
|
||||
for c in components {
|
||||
match c {
|
||||
Component::Region(r1) => {
|
||||
if !r1.is_bound() {
|
||||
collected.insert(ty::OutlivesPredicate(r1.into(), r2));
|
||||
}
|
||||
}
|
||||
|
||||
Component::Param(p) => {
|
||||
let ty = Ty::new_param(cx, p);
|
||||
collected.insert(ty::OutlivesPredicate(ty.into(), r2));
|
||||
}
|
||||
|
||||
Component::Placeholder(p) => {
|
||||
let ty = Ty::new_placeholder(cx, p);
|
||||
collected.insert(ty::OutlivesPredicate(ty.into(), r2));
|
||||
}
|
||||
|
||||
Component::Alias(alias_ty) => {
|
||||
collected.insert(ty::OutlivesPredicate(alias_ty.to_ty(cx).into(), r2));
|
||||
}
|
||||
|
||||
Component::UnresolvedInferenceVariable(_) | Component::EscapingAlias(_) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nothing to elaborate for a region.
|
||||
ty::GenericArgKind::Lifetime(_) => {}
|
||||
// Consts don't really participate in outlives.
|
||||
ty::GenericArgKind::Const(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
collected
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,6 +146,13 @@ pub trait Interner:
|
|||
type BoundRegion: BoundVarLike<Self>;
|
||||
type PlaceholderRegion: PlaceholderLike<Self, Bound = Self::BoundRegion>;
|
||||
|
||||
type RegionAssumptions: Copy
|
||||
+ Debug
|
||||
+ Hash
|
||||
+ Eq
|
||||
+ SliceLike<Item = ty::OutlivesPredicate<Self, Self::GenericArg>>
|
||||
+ TypeFoldable<Self>;
|
||||
|
||||
// Predicates
|
||||
type ParamEnv: ParamEnv<Self>;
|
||||
type Predicate: Predicate<Self>;
|
||||
|
|
|
|||
|
|
@ -1150,4 +1150,5 @@ pub struct FnHeader<I: Interner> {
|
|||
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
|
||||
pub struct CoroutineWitnessTypes<I: Interner> {
|
||||
pub types: I::Tys,
|
||||
pub assumptions: I::RegionAssumptions,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1021,7 +1021,6 @@ ui/foreign/issue-91370-foreign-fn-block-impl.rs
|
|||
ui/foreign/issue-99276-same-type-lifetimes.rs
|
||||
ui/function-pointer/issue-102289.rs
|
||||
ui/functions-closures/closure-expected-type/issue-38714.rs
|
||||
ui/generic-associated-types/bugs/issue-100013.rs
|
||||
ui/generic-associated-types/bugs/issue-80626.rs
|
||||
ui/generic-associated-types/bugs/issue-87735.rs
|
||||
ui/generic-associated-types/bugs/issue-87755.rs
|
||||
|
|
@ -1099,7 +1098,6 @@ ui/generic-associated-types/issue-90729.rs
|
|||
ui/generic-associated-types/issue-91139.rs
|
||||
ui/generic-associated-types/issue-91883.rs
|
||||
ui/generic-associated-types/issue-92033.rs
|
||||
ui/generic-associated-types/issue-92096.rs
|
||||
ui/generic-associated-types/issue-92280.rs
|
||||
ui/generic-associated-types/issue-92954.rs
|
||||
ui/generic-associated-types/issue-93141.rs
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/drop-tracking-unresolved-typeck-results.rs:98:5
|
||||
--> $DIR/drop-tracking-unresolved-typeck-results.rs:102:5
|
||||
|
|
||||
LL | / send(async {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
|
||||
LL | | });
|
||||
| |______^ implementation of `FnOnce` is not general enough
|
||||
|
|
@ -12,11 +10,9 @@ LL | | });
|
|||
= note: ...but it actually implements `FnOnce<(&(),)>`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/drop-tracking-unresolved-typeck-results.rs:98:5
|
||||
--> $DIR/drop-tracking-unresolved-typeck-results.rs:102:5
|
||||
|
|
||||
LL | / send(async {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
|
||||
LL | | });
|
||||
| |______^ implementation of `FnOnce` is not general enough
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
//@ incremental
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::future::*;
|
||||
use std::marker::PhantomData;
|
||||
|
|
@ -96,8 +100,6 @@ impl<St: ?Sized + Stream + Unpin> Future for Next<'_, St> {
|
|||
|
||||
fn main() {
|
||||
send(async {
|
||||
//~^ ERROR implementation of `FnOnce` is not general enough
|
||||
//~| ERROR implementation of `FnOnce` is not general enough
|
||||
Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/higher-ranked-auto-trait-1.rs:37:5
|
||||
|
|
||||
LL | / async {
|
||||
LL | | let _y = &();
|
||||
LL | | let _x = filter(FilterMap {
|
||||
LL | | f: || async move { *_y },
|
||||
... |
|
||||
LL | | drop(_x);
|
||||
LL | | }
|
||||
| |_____^ one type is more general than the other
|
||||
|
|
||||
= note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
|
||||
found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
|
||||
= note: no two async blocks, even if identical, have the same type
|
||||
= help: consider pinning your async block and casting it to a trait object
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/higher-ranked-auto-trait-1.rs:37:5
|
||||
|
|
||||
LL | / async {
|
||||
LL | | let _y = &();
|
||||
LL | | let _x = filter(FilterMap {
|
||||
LL | | f: || async move { *_y },
|
||||
... |
|
||||
LL | | drop(_x);
|
||||
LL | | }
|
||||
| |_____^ one type is more general than the other
|
||||
|
|
||||
= note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
|
||||
found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
|
||||
= note: no two async blocks, even if identical, have the same type
|
||||
= help: consider pinning your async block and casting it to a trait object
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
48
tests/ui/async-await/higher-ranked-auto-trait-1.rs
Normal file
48
tests/ui/async-await/higher-ranked-auto-trait-1.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/79648#issuecomment-749127947>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
trait Stream {
|
||||
type Item;
|
||||
}
|
||||
|
||||
struct Filter<St: Stream> {
|
||||
pending_item: St::Item,
|
||||
}
|
||||
|
||||
fn filter<St: Stream>(_: St) -> Filter<St> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
struct FilterMap<Fut, F> {
|
||||
f: F,
|
||||
pending: PhantomData<Fut>,
|
||||
}
|
||||
|
||||
impl<Fut, F> Stream for FilterMap<Fut, F>
|
||||
where
|
||||
F: FnMut() -> Fut,
|
||||
Fut: Future,
|
||||
{
|
||||
type Item = ();
|
||||
}
|
||||
|
||||
pub fn get_foo() -> impl Future + Send {
|
||||
async {
|
||||
let _y = &();
|
||||
let _x = filter(FilterMap {
|
||||
f: || async move { *_y },
|
||||
pending: PhantomData,
|
||||
});
|
||||
async {}.await;
|
||||
drop(_x);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
error: implementation of `Foo` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
||||
|
|
||||
LL | Box::new(async move { get_foo(x).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
||||
|
|
||||
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Foo` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
||||
|
|
||||
LL | Box::new(async move { get_foo(x).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
||||
|
|
||||
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
error: implementation of `Foo` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
||||
|
|
||||
LL | Box::new(async move { get_foo(x).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
||||
|
|
||||
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Foo` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
||||
|
|
||||
LL | Box::new(async move { get_foo(x).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
||||
|
|
||||
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
35
tests/ui/async-await/higher-ranked-auto-trait-10.rs
Normal file
35
tests/ui/async-await/higher-ranked-auto-trait-10.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/92415#issue-1090723521>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] known-bug: unknown
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::any::Any;
|
||||
use std::future::Future;
|
||||
|
||||
trait Foo<'a>: Sized {
|
||||
type Error;
|
||||
fn foo(x: &'a str) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
impl<'a> Foo<'a> for &'a str {
|
||||
type Error = ();
|
||||
|
||||
fn foo(x: &'a str) -> Result<Self, Self::Error> {
|
||||
Ok(x)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_foo<'a, T>(x: &'a str) -> Result<T, <T as Foo<'a>>::Error>
|
||||
where
|
||||
T: Foo<'a>,
|
||||
{
|
||||
Foo::foo(x)
|
||||
}
|
||||
|
||||
fn bar<'a>(x: &'a str) -> Box<dyn Future<Output = Result<&'a str, ()>> + Send + 'a> {
|
||||
Box::new(async move { get_foo(x).await })
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
error: lifetime may not live long enough
|
||||
--> $DIR/higher-ranked-auto-trait-11.rs:27:9
|
||||
|
|
||||
LL | impl<'a, T> Foo<'a> for MyType<T>
|
||||
| -- lifetime `'a` defined here
|
||||
...
|
||||
LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ coercion requires that `'a` must outlive `'static`
|
||||
|
||||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-11.rs:27:9
|
||||
|
|
||||
LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
||||
|
|
||||
= note: `Send` would have to be implemented for the type `<T as Foo<'0>>::Future`, for any lifetime `'0`...
|
||||
= note: ...but `Send` is actually implemented for the type `<T as Foo<'1>>::Future`, for some specific lifetime `'1`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
error: lifetime may not live long enough
|
||||
--> $DIR/higher-ranked-auto-trait-11.rs:27:9
|
||||
|
|
||||
LL | impl<'a, T> Foo<'a> for MyType<T>
|
||||
| -- lifetime `'a` defined here
|
||||
...
|
||||
LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ coercion requires that `'a` must outlive `'static`
|
||||
|
||||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-11.rs:27:9
|
||||
|
|
||||
LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
||||
|
|
||||
= note: `Send` would have to be implemented for the type `<T as Foo<'0>>::Future`, for any lifetime `'0`...
|
||||
= note: ...but `Send` is actually implemented for the type `<T as Foo<'1>>::Future`, for some specific lifetime `'1`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
31
tests/ui/async-await/higher-ranked-auto-trait-11.rs
Normal file
31
tests/ui/async-await/higher-ranked-auto-trait-11.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/60658#issuecomment-1509321859>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] known-bug: unknown
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use core::pin::Pin;
|
||||
use std::future::Future;
|
||||
|
||||
pub trait Foo<'a> {
|
||||
type Future: Future<Output = ()>;
|
||||
|
||||
fn foo() -> Self::Future;
|
||||
}
|
||||
|
||||
struct MyType<T>(T);
|
||||
|
||||
impl<'a, T> Foo<'a> for MyType<T>
|
||||
where
|
||||
T: Foo<'a>,
|
||||
T::Future: Send,
|
||||
{
|
||||
type Future = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
|
||||
|
||||
fn foo() -> Self::Future {
|
||||
Box::pin(async move { <T as Foo<'a>>::foo().await })
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
error: implementation of `Robot` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-12.rs:31:20
|
||||
|
|
||||
LL | let _my_task = this_is_send(async move {
|
||||
| ____________________^
|
||||
LL | | let _my_iter = IRobot {
|
||||
LL | | id: 32,
|
||||
LL | | robot: source,
|
||||
LL | | };
|
||||
LL | | yield_now().await;
|
||||
LL | | });
|
||||
| |______^ implementation of `Robot` is not general enough
|
||||
|
|
||||
= note: `Box<(dyn Robot<Id = u32> + Send + '0)>` must implement `Robot`, for any lifetime `'0`...
|
||||
= note: ...but `Robot` is actually implemented for the type `Box<(dyn Robot<Id = u32> + Send + 'static)>`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
40
tests/ui/async-await/higher-ranked-auto-trait-12.rs
Normal file
40
tests/ui/async-await/higher-ranked-auto-trait-12.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/71671#issuecomment-848994782>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
pub trait Robot {
|
||||
type Id;
|
||||
}
|
||||
|
||||
pub type DynRobot = Box<dyn Robot<Id = u32> + Send>;
|
||||
|
||||
impl Robot for DynRobot {
|
||||
type Id = u32;
|
||||
}
|
||||
|
||||
struct IRobot<R: Robot> {
|
||||
id: R::Id,
|
||||
robot: R,
|
||||
}
|
||||
|
||||
// stand-in for tokio::spawn
|
||||
fn this_is_send<T: Send>(value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
async fn yield_now() {}
|
||||
|
||||
fn test(source: DynRobot) {
|
||||
let _my_task = this_is_send(async move {
|
||||
let _my_iter = IRobot {
|
||||
id: 32,
|
||||
robot: source,
|
||||
};
|
||||
yield_now().await;
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Callable` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough
|
||||
|
|
||||
= note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`...
|
||||
= note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1`
|
||||
|
||||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Getter` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
|
||||
|
|
||||
= note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Callable` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
||||
|
|
||||
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough
|
||||
|
|
||||
= note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`...
|
||||
= note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
68
tests/ui/async-await/higher-ranked-auto-trait-13.rs
Normal file
68
tests/ui/async-await/higher-ranked-auto-trait-13.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/114046#issue-1819720359>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] known-bug: unknown
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
trait Callable<'a>: Send + Sync {
|
||||
fn callable(data: &'a [u8]);
|
||||
}
|
||||
|
||||
trait Getter<'a>: Send + Sync {
|
||||
type ItemSize: Send + Sync;
|
||||
|
||||
fn get(data: &'a [u8]);
|
||||
}
|
||||
|
||||
struct List<'a, A: Getter<'a>> {
|
||||
data: &'a [u8],
|
||||
item_size: A::ItemSize, // Removing this member causes the code to compile
|
||||
phantom: PhantomData<A>,
|
||||
}
|
||||
|
||||
struct GetterImpl<'a, T: Callable<'a> + 'a> {
|
||||
p: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Callable<'a> + 'a> Getter<'a> for GetterImpl<'a, T> {
|
||||
type ItemSize = ();
|
||||
|
||||
fn get(data: &'a [u8]) {
|
||||
<T>::callable(data);
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstructableImpl<'a> {
|
||||
_data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Callable<'a> for ConstructableImpl<'a> {
|
||||
fn callable(_: &'a [u8]) {}
|
||||
}
|
||||
|
||||
struct StructWithLifetime<'a> {
|
||||
marker: &'a PhantomData<u8>,
|
||||
}
|
||||
|
||||
async fn async_method() {}
|
||||
|
||||
fn assert_send(_: impl Send + Sync) {}
|
||||
|
||||
// This async method ought to be send, but is not
|
||||
async fn my_send_async_method(_struct_with_lifetime: &mut StructWithLifetime<'_>, data: &Vec<u8>) {
|
||||
let _named =
|
||||
List::<'_, GetterImpl<ConstructableImpl<'_>>> { data, item_size: (), phantom: PhantomData };
|
||||
// Moving the await point above the constructed of _named, causes
|
||||
// the method to become send, even though _named is Send + Sync
|
||||
async_method().await;
|
||||
assert_send(_named);
|
||||
}
|
||||
|
||||
fn dummy(struct_with_lifetime: &mut StructWithLifetime<'_>, data: &Vec<u8>) {
|
||||
assert_send(my_send_async_method(struct_with_lifetime, data));
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-14.rs:20:5
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | let xs = unique_x.union(&cached)
|
||||
LL | | // .copied() // works
|
||||
LL | | .map(|x| *x) // error
|
||||
LL | | ;
|
||||
LL | | let blah = val.blah(xs.into_iter()).await;
|
||||
LL | | }
|
||||
| |_____^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'0 u32) -> u32` must implement `FnOnce<(&'1 u32,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&u32,)>`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-14.rs:20:5
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | let xs = unique_x.union(&cached)
|
||||
LL | | // .copied() // works
|
||||
LL | | .map(|x| *x) // error
|
||||
LL | | ;
|
||||
LL | | let blah = val.blah(xs.into_iter()).await;
|
||||
LL | | }
|
||||
| |_____^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'0 u32) -> u32` must implement `FnOnce<(&'1 u32,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&u32,)>`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
29
tests/ui/async-await/higher-ranked-auto-trait-14.rs
Normal file
29
tests/ui/async-await/higher-ranked-auto-trait-14.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/124757#issue-2279603232>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::future::Future;
|
||||
|
||||
trait MyTrait {
|
||||
fn blah(&self, x: impl Iterator<Item = u32>) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
fn foo<T: MyTrait + Send + Sync>(
|
||||
val: T,
|
||||
unique_x: HashSet<u32>,
|
||||
) -> impl Future<Output = ()> + Send {
|
||||
let cached = HashSet::new();
|
||||
async move {
|
||||
let xs = unique_x.union(&cached)
|
||||
// .copied() // works
|
||||
.map(|x| *x) // error
|
||||
;
|
||||
let blah = val.blah(xs.into_iter()).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-15.rs:20:5
|
||||
|
|
||||
LL | require_send(future);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-15.rs:20:5
|
||||
|
|
||||
LL | require_send(future);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
21
tests/ui/async-await/higher-ranked-auto-trait-15.rs
Normal file
21
tests/ui/async-await/higher-ranked-auto-trait-15.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/126044#issuecomment-2154313449>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
async fn listen() {
|
||||
let things: Vec<Vec<i32>> = vec![];
|
||||
for _ in things.iter().map(|n| n.iter()).flatten() {
|
||||
// comment this line and everything compiles
|
||||
async {}.await;
|
||||
}
|
||||
}
|
||||
|
||||
fn require_send<T: Send>(_x: T) {}
|
||||
|
||||
fn main() {
|
||||
let future = listen();
|
||||
require_send(future);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: implementation of `AsyncFnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-16.rs:18:5
|
||||
|
|
||||
LL | / assert_send(async {
|
||||
LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
|
||||
LL | | });
|
||||
| |______^ implementation of `AsyncFnOnce` is not general enough
|
||||
|
|
||||
= note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
|
||||
|
||||
error: implementation of `AsyncFnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-16.rs:18:5
|
||||
|
|
||||
LL | / assert_send(async {
|
||||
LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
|
||||
LL | | });
|
||||
| |______^ implementation of `AsyncFnOnce` is not general enough
|
||||
|
|
||||
= note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: implementation of `AsyncFnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-16.rs:18:5
|
||||
|
|
||||
LL | / assert_send(async {
|
||||
LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
|
||||
LL | | });
|
||||
| |______^ implementation of `AsyncFnOnce` is not general enough
|
||||
|
|
||||
= note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
|
||||
|
||||
error: implementation of `AsyncFnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-16.rs:18:5
|
||||
|
|
||||
LL | / assert_send(async {
|
||||
LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
|
||||
LL | | });
|
||||
| |______^ implementation of `AsyncFnOnce` is not general enough
|
||||
|
|
||||
= note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
23
tests/ui/async-await/higher-ranked-auto-trait-16.rs
Normal file
23
tests/ui/async-await/higher-ranked-auto-trait-16.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/126350#issue-2349492101>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] known-bug: unknown
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
fn assert_send<T: Send>(_: T) {}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Ctxt<'a>(&'a ());
|
||||
|
||||
async fn commit_if_ok<'a>(ctxt: &mut Ctxt<'a>, f: impl AsyncFnOnce(&mut Ctxt<'a>)) {
|
||||
f(&mut ctxt.clone()).await;
|
||||
}
|
||||
|
||||
fn operation(mut ctxt: Ctxt<'_>) {
|
||||
assert_send(async {
|
||||
commit_if_ok(&mut ctxt, async |_| todo!()).await;
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-17.rs:12:5
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | let iter = Adaptor::new(a.iter().map(|_: &()| {}));
|
||||
LL | | std::future::pending::<()>().await;
|
||||
LL | | drop(iter);
|
||||
LL | | }
|
||||
| |_____^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&(),)>`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-17.rs:12:5
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | let iter = Adaptor::new(a.iter().map(|_: &()| {}));
|
||||
LL | | std::future::pending::<()>().await;
|
||||
LL | | drop(iter);
|
||||
LL | | }
|
||||
| |_____^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&(),)>`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
30
tests/ui/async-await/higher-ranked-auto-trait-17.rs
Normal file
30
tests/ui/async-await/higher-ranked-auto-trait-17.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/114177#issue-1826550174>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
// Using `impl Future` instead of `async to ensure that the Future is Send.
|
||||
//
|
||||
// In the original code `a` would be `&[T]`. For more minimization I've removed the reference.
|
||||
fn foo(a: [(); 0]) -> impl std::future::Future<Output = ()> + Send {
|
||||
async move {
|
||||
let iter = Adaptor::new(a.iter().map(|_: &()| {}));
|
||||
std::future::pending::<()>().await;
|
||||
drop(iter);
|
||||
}
|
||||
}
|
||||
|
||||
struct Adaptor<T: Iterator> {
|
||||
iter: T,
|
||||
v: T::Item,
|
||||
}
|
||||
|
||||
impl<T: Iterator> Adaptor<T> {
|
||||
pub fn new(_: T) -> Self {
|
||||
Self { iter: todo!(), v: todo!() }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-auto-trait-2.rs:16:9
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
|
||||
LL | | self.run(t).await;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-auto-trait-2.rs:16:9
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
|
||||
LL | | self.run(t).await;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-auto-trait-2.rs:16:9
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
|
||||
LL | | self.run(t).await;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-auto-trait-2.rs:16:9
|
||||
|
|
||||
LL | / async move {
|
||||
LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
|
||||
LL | | self.run(t).await;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
23
tests/ui/async-await/higher-ranked-auto-trait-2.rs
Normal file
23
tests/ui/async-await/higher-ranked-auto-trait-2.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/111105#issue-1692860759>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
pub trait Foo: Sync {
|
||||
fn run<'a, 'b: 'a, T: Sync>(&'a self, _: &'b T) -> impl Future<Output = ()> + 'a + Send;
|
||||
}
|
||||
|
||||
pub trait FooExt: Foo {
|
||||
fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a + Send {
|
||||
async move {
|
||||
// asks for an unspecified lifetime to outlive itself? weird diagnostics
|
||||
self.run(t).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-auto-trait-3.rs:66:9
|
||||
|
|
||||
LL | / async {
|
||||
LL | | self.fi_2.get_iter(cx).await;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
72
tests/ui/async-await/higher-ranked-auto-trait-3.rs
Normal file
72
tests/ui/async-await/higher-ranked-auto-trait-3.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/100013#issue-1323807923>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
pub trait FutureIterator: 'static {
|
||||
type Iterator;
|
||||
|
||||
type Future<'s, 'cx>: Future<Output = Self::Iterator> + Send + 'cx
|
||||
where
|
||||
's: 'cx;
|
||||
|
||||
fn get_iter<'s, 'cx>(&'s self, info: &'cx ()) -> Self::Future<'s, 'cx>;
|
||||
}
|
||||
|
||||
trait IterCaller: 'static {
|
||||
type Future1<'cx>: Future<Output = ()> + Send + 'cx;
|
||||
type Future2<'cx>: Future<Output = ()> + Send + 'cx;
|
||||
|
||||
fn call_1<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future1<'cx>
|
||||
where
|
||||
's: 'cx;
|
||||
fn call_2<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future2<'cx>
|
||||
where
|
||||
's: 'cx;
|
||||
}
|
||||
|
||||
struct UseIter<FI1, FI2> {
|
||||
fi_1: FI1,
|
||||
fi_2: FI2,
|
||||
}
|
||||
|
||||
impl<FI1, FI2> IterCaller for UseIter<FI1, FI2>
|
||||
where
|
||||
FI1: FutureIterator + 'static + Send + Sync,
|
||||
for<'s, 'cx> FI1::Future<'s, 'cx>: Send,
|
||||
FI2: FutureIterator + 'static + Send + Sync,
|
||||
{
|
||||
type Future1<'cx> = impl Future<Output = ()> + Send + 'cx
|
||||
where
|
||||
Self: 'cx;
|
||||
|
||||
type Future2<'cx> = impl Future<Output = ()> + Send + 'cx
|
||||
where
|
||||
Self: 'cx;
|
||||
|
||||
fn call_1<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future1<'cx>
|
||||
where
|
||||
's: 'cx,
|
||||
{
|
||||
async {
|
||||
self.fi_1.get_iter(cx).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn call_2<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future2<'cx>
|
||||
where
|
||||
's: 'cx,
|
||||
{
|
||||
async {
|
||||
self.fi_2.get_iter(cx).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
error: higher-ranked lifetime error
|
||||
--> $DIR/higher-ranked-auto-trait-4.rs:29:5
|
||||
|
|
||||
LL | / async {
|
||||
LL | | let _ = evil_function::<dyn BoringTrait, _>().await;
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
34
tests/ui/async-await/higher-ranked-auto-trait-4.rs
Normal file
34
tests/ui/async-await/higher-ranked-auto-trait-4.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/102211#issuecomment-2891975128>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait BoringTrait {}
|
||||
|
||||
trait TraitWithAssocType<I> {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl<T> TraitWithAssocType<()> for T
|
||||
where
|
||||
T: ?Sized + 'static,
|
||||
{
|
||||
type Assoc = ();
|
||||
}
|
||||
|
||||
fn evil_function<T: TraitWithAssocType<I> + ?Sized, I>()
|
||||
-> impl Future<Output = Result<(), T::Assoc>> {
|
||||
async { Ok(()) }
|
||||
}
|
||||
|
||||
fn fails_to_compile() -> impl std::future::Future<Output = ()> + Send {
|
||||
async {
|
||||
let _ = evil_function::<dyn BoringTrait, _>().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-5.rs:13:5
|
||||
|
|
||||
LL | / assert_send(async {
|
||||
LL | | call_me.call().await;
|
||||
LL | | });
|
||||
| |______^ implementation of `Send` is not general enough
|
||||
|
|
||||
= note: `Send` would have to be implemented for the type `&'0 str`, for any lifetime `'0`...
|
||||
= note: ...but `Send` is actually implemented for the type `&'1 str`, for some specific lifetime `'1`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
54
tests/ui/async-await/higher-ranked-auto-trait-5.rs
Normal file
54
tests/ui/async-await/higher-ranked-auto-trait-5.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/130113#issue-2512517191>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
fn main() {
|
||||
let call_me = Wrap(CallMeImpl { value: "test" });
|
||||
|
||||
assert_send(async {
|
||||
call_me.call().await;
|
||||
});
|
||||
}
|
||||
|
||||
pub fn assert_send<F>(_future: F)
|
||||
where
|
||||
F: Future + Send,
|
||||
{
|
||||
}
|
||||
|
||||
pub trait CallMe {
|
||||
fn call(&self) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
struct Wrap<T>(T);
|
||||
|
||||
impl<S> CallMe for Wrap<S>
|
||||
where
|
||||
S: CallMe + Send,
|
||||
{
|
||||
// adding `+ Send` to this RPIT fixes the issue
|
||||
fn call(&self) -> impl Future<Output = ()> {
|
||||
self.0.call()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CallMeImpl<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<T> CallMe for CallMeImpl<T>
|
||||
where
|
||||
// Can replace `Send` by `ToString`, `Clone`, whatever. When removing the
|
||||
// `Send` bound, the compiler produces a higher-ranked lifetime error.
|
||||
T: Send + 'static,
|
||||
{
|
||||
fn call(&self) -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/higher-ranked-auto-trait-6.rs:16:5
|
||||
|
|
||||
LL | Box::new(async { new(|| async { f().await }).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
= note: no two async blocks, even if identical, have the same type
|
||||
= help: consider pinning your async block and casting it to a trait object
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/higher-ranked-auto-trait-6.rs:16:5
|
||||
|
|
||||
LL | Box::new(async { new(|| async { f().await }).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
= note: no two async blocks, even if identical, have the same type
|
||||
= help: consider pinning your async block and casting it to a trait object
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/higher-ranked-auto-trait-6.rs:16:5
|
||||
|
|
||||
LL | Box::new(async { new(|| async { f().await }).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
= note: no two async blocks, even if identical, have the same type
|
||||
= help: consider pinning your async block and casting it to a trait object
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/higher-ranked-auto-trait-6.rs:16:5
|
||||
|
|
||||
LL | Box::new(async { new(|| async { f().await }).await })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
|
||||
= note: no two async blocks, even if identical, have the same type
|
||||
= help: consider pinning your async block and casting it to a trait object
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
59
tests/ui/async-await/higher-ranked-auto-trait-6.rs
Normal file
59
tests/ui/async-await/higher-ranked-auto-trait-6.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/82921#issue-825114180>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomData;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
async fn f() {}
|
||||
|
||||
pub fn fail<'a>() -> Box<dyn Future<Output = ()> + Send + 'a> {
|
||||
Box::new(async { new(|| async { f().await }).await })
|
||||
}
|
||||
|
||||
fn new<A, B>(_a: A) -> F<A, B>
|
||||
where
|
||||
A: Fn() -> B,
|
||||
{
|
||||
F { _i: PhantomData }
|
||||
}
|
||||
|
||||
trait Stream {
|
||||
type Item;
|
||||
}
|
||||
|
||||
struct T<A, B> {
|
||||
_a: PhantomData<A>,
|
||||
_b: PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<A, B> Stream for T<A, B>
|
||||
where
|
||||
A: Fn() -> B,
|
||||
{
|
||||
type Item = B;
|
||||
}
|
||||
|
||||
struct F<A, B>
|
||||
where
|
||||
A: Fn() -> B,
|
||||
{
|
||||
_i: PhantomData<<T<A, B> as Stream>::Item>,
|
||||
}
|
||||
|
||||
impl<A, B> Future for F<A, B>
|
||||
where
|
||||
A: Fn() -> B,
|
||||
{
|
||||
type Output = ();
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
error: `S` does not live long enough
|
||||
--> $DIR/higher-ranked-auto-trait-7.rs:26:5
|
||||
|
|
||||
LL | future::<'a, S, _>(async move {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
33
tests/ui/async-await/higher-ranked-auto-trait-7.rs
Normal file
33
tests/ui/async-await/higher-ranked-auto-trait-7.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/90696#issuecomment-963375847>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
#![allow(dropping_copy_types)]
|
||||
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
trait Trait {
|
||||
type Associated<'a>: Send
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
fn future<'a, S: Trait + 'a, F>(f: F) -> F
|
||||
where
|
||||
F: Future<Output = ()> + Send,
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
fn foo<'a, S: Trait + 'a>() {
|
||||
future::<'a, S, _>(async move {
|
||||
let result: PhantomData<S::Associated<'a>> = PhantomData;
|
||||
async {}.await;
|
||||
drop(result);
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
error: higher-ranked lifetime error
|
||||
--> $DIR/higher-ranked-auto-trait-8.rs:26:5
|
||||
|
|
||||
LL | needs_send(use_my_struct(second_struct)); // ERROR
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: could not prove `impl Future<Output = ()>: Send`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
27
tests/ui/async-await/higher-ranked-auto-trait-8.rs
Normal file
27
tests/ui/async-await/higher-ranked-auto-trait-8.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/64552#issuecomment-532801232>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
fn needs_send<T: Send>(_val: T) {}
|
||||
async fn use_async<T>(_val: T) {}
|
||||
|
||||
struct MyStruct<'a, T: 'a> {
|
||||
val: &'a T,
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: 'a> Send for MyStruct<'a, T> {}
|
||||
|
||||
async fn use_my_struct(val: MyStruct<'static, &'static u8>) {
|
||||
use_async(val).await;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let first_struct: MyStruct<'static, &'static u8> = MyStruct { val: &&26 };
|
||||
let second_struct: MyStruct<'static, &'static u8> = MyStruct { val: &&27 };
|
||||
|
||||
needs_send(first_struct);
|
||||
needs_send(use_my_struct(second_struct)); // ERROR
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
error: implementation of `Debug` is not general enough
|
||||
--> $DIR/higher-ranked-auto-trait-9.rs:43:50
|
||||
|
|
||||
LL | let fut: &(dyn Future<Output = ()> + Send) = &fut as _;
|
||||
| ^^^^^^^^^ implementation of `Debug` is not general enough
|
||||
|
|
||||
= note: `(dyn Any + '0)` must implement `Debug`, for any lifetime `'0`...
|
||||
= note: ...but `Debug` is actually implemented for the type `(dyn Any + 'static)`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
44
tests/ui/async-await/higher-ranked-auto-trait-9.rs
Normal file
44
tests/ui/async-await/higher-ranked-auto-trait-9.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Repro for <https://github.com/rust-lang/rust/issues/87425#issue-952059416>.
|
||||
//@ edition: 2021
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
|
||||
pub trait Foo {
|
||||
type Item;
|
||||
}
|
||||
|
||||
impl<F, I> Foo for F
|
||||
where
|
||||
Self: FnOnce() -> I,
|
||||
I: fmt::Debug,
|
||||
{
|
||||
type Item = I;
|
||||
}
|
||||
|
||||
async fn foo_item<F: Foo>(_: F) -> F::Item {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let fut = async {
|
||||
let callback = || -> Box<dyn Any> { unimplemented!() };
|
||||
|
||||
// Using plain fn instead of a closure fixes the error,
|
||||
// though you obviously can't capture any state...
|
||||
// fn callback() -> Box<dyn Any> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
foo_item(callback).await;
|
||||
};
|
||||
|
||||
// Removing `+ Send` bound also fixes the error,
|
||||
// though at the cost of loosing `Send`ability...
|
||||
let fut: &(dyn Future<Output = ()> + Send) = &fut as _;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/issue-110963-early.rs:14:5
|
||||
--> $DIR/issue-110963-early.rs:17:5
|
||||
|
|
||||
LL | / spawn(async move {
|
||||
LL | | let mut hc = hc;
|
||||
|
|
@ -13,7 +13,7 @@ LL | | });
|
|||
= note: ...but `Send` is actually implemented for the type `impl Future<Output = bool> { <HC as HealthCheck>::check<'2>(..) }`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/issue-110963-early.rs:14:5
|
||||
--> $DIR/issue-110963-early.rs:17:5
|
||||
|
|
||||
LL | / spawn(async move {
|
||||
LL | | let mut hc = hc;
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
//@ edition: 2021
|
||||
//@ known-bug: #110963
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
#![feature(return_type_notation)]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
error: lifetime bound not satisfied
|
||||
--> $DIR/issue-100013.rs:15:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/issue-100013.rs:22:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/issue-100013.rs:23:17
|
||||
|
|
||||
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
|
||||
| -- -- lifetime `'b` defined here
|
||||
| |
|
||||
| lifetime `'a` defined here
|
||||
LL | async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'b`
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/issue-100013.rs:29:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-coroutine-param-outlives-2.rs:14:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/higher-ranked-coroutine-param-outlives-2.rs:21:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
//@ check-fail
|
||||
//@ known-bug: #100013
|
||||
//@ edition: 2021
|
||||
|
||||
// We really should accept this, but we need implied bounds between the regions
|
||||
// in a coroutine interior.
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
pub trait FutureIterator {
|
||||
type Future<'s, 'cx>: Send
|
||||
|
|
@ -18,14 +17,7 @@ fn call<I: FutureIterator>() -> impl Send {
|
|||
}
|
||||
}
|
||||
|
||||
fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
|
||||
async { // a coroutine checked for autotrait impl `Send`
|
||||
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
async {}.await; // a yield point
|
||||
}
|
||||
}
|
||||
|
||||
fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
|
||||
fn call2<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
|
||||
async { // a coroutine checked for autotrait impl `Send`
|
||||
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
async {}.await; // a yield point
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
error: `C` does not live long enough
|
||||
--> $DIR/issue-92096.rs:17:5
|
||||
--> $DIR/higher-ranked-coroutine-param-outlives.rs:21:5
|
||||
|
|
||||
LL | async move { c.connect().await }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
//@ edition:2018
|
||||
//@ revisions: assumptions no_assumptions
|
||||
//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
|
||||
//@[assumptions] check-pass
|
||||
//@[no_assumptions] known-bug: #110338
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait Client {
|
||||
type Connecting<'a>: Future + Send
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn connect(&'_ self) -> Self::Connecting<'_>;
|
||||
}
|
||||
|
||||
fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
|
||||
where
|
||||
C: Client + Send + Sync,
|
||||
{
|
||||
async move { c.connect().await }
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
//@ edition:2018
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait Client {
|
||||
type Connecting<'a>: Future + Send
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn connect(&'_ self) -> Self::Connecting<'_>;
|
||||
}
|
||||
|
||||
fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
|
||||
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() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue