diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index ca636a8c9992c..9bb96b94506a4 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -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>, ) { 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 diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f877e5eaadb91..d500088c259a6 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -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); diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 301f5de9424e5..99a633e2b7d1f 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -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>, Span>; +pub(crate) type RequiredPredicates<'tcx> = FxIndexMap, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index d5d49e3188aa8..6be53c948c84a 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -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>, region_constraints: &RegionConstraintData<'tcx>, + assumptions: Vec>, ) -> 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 } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index cc3ad921489fc..2d269e320b64d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> { /// that all type inference variables have been bound and so forth. region_obligations: Vec>, + /// 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>, + /// 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) diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index cb5a33c5c972a..47b738a407967 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -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>, + /// 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>, } /// "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>, extra_bounds: impl IntoIterator>, + higher_ranked_assumptions: FxHashSet>, ) -> 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> { + &self.higher_ranked_assumptions + } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 2a4544c1140d1..19911bfbd48b8 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -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::>::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() } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 43504bd77c18b..a8520c0e71dd1 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -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> { + 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( diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 34e649afedc69..40e4c329446da 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> { RegionUnificationTable(sv::UndoLog>>), ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, + PushRegionAssumption, } macro_rules! impl_from { @@ -77,6 +78,9 @@ impl<'tcx> Rollback> 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(); + } } } } diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 2bbc48b633c85..4fe4c2dadee11 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> { #[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec>, + pub assumptions: Vec>, } 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> { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 935cc8895740b..f605da24bc491 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -988,7 +988,7 @@ rustc_queries! { } query coroutine_hidden_types( - def_id: DefId + def_id: DefId, ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { desc { "looking up the hidden types stored across await points in a coroutine" } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 98b2ce01d89b2..5570ba1d9127f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -162,6 +162,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type BoundRegion = ty::BoundRegion; type PlaceholderRegion = ty::PlaceholderRegion; + type RegionAssumptions = &'tcx ty::List>; + type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; @@ -874,6 +876,7 @@ pub struct CtxtInterners<'tcx> { offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>, patterns: InternedSet<'tcx, List>>, + outlives: InternedSet<'tcx, List>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -911,6 +914,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), } } @@ -2692,6 +2696,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> { @@ -3107,6 +3112,17 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs)) } + pub fn mk_outlives_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply< + ty::ArgOutlivesPredicate<'tcx>, + &'tcx ty::List>, + >, + { + 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] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6e8f1e8fdd5bf..a7cde2ad48547 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -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, diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index bc2ac42b6b1f8..26f4282249e5b 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -26,6 +26,7 @@ pub type SubtypePredicate<'tcx> = ir::SubtypePredicate>; pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate, 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>>; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index af9c98bd87d0e..ab31d94340809 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -802,4 +802,5 @@ list_fold! { &'tcx ty::List> : mk_poly_existential_predicates, &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, + &'tcx ty::List> : mk_outlives, } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 626262c844257..2bdde2f887a30 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2256,6 +2256,8 @@ options! { environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), has_thread_local: Option = (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], diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 068e90b00b8d1..2b33b8ac9f86c 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -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, ` 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, ) } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 1a24254d57fbe..17429e15cce06 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -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>> { + fn make_deduplicated_outlives_constraints(&self) -> Vec> { // 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(); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index ce5a4edeaaa06..f50f01a285b56 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -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( diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 59d3ac21387f5..53518038f8d5e 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -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); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 3b549244431d2..f027ba1c5cbfd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -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() { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 4bdf04311a04f..018e9748cf068 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -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 diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index ee8cef2027991..488094b15ac60 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -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) }) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2e65750db25de..e05782e41fb39 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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::*; @@ -2247,10 +2248,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> { /// Zed 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>>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { Ok(match *t.kind() { ty::Uint(_) | ty::Int(_) @@ -2261,17 +2262,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(..) @@ -2283,30 +2293,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 @@ -2314,16 +2335,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`, 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) { @@ -2333,7 +2361,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)); } @@ -3113,3 +3144,9 @@ pub(crate) enum ProjectionMatchesProjection { Ambiguous, No, } + +#[derive(Clone, Debug, TypeFoldable, TypeVisitable)] +pub(crate) struct AutoImplConstituents<'tcx> { + pub types: Vec>, + pub assumptions: Vec>, +} diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 447e13126ccdc..87d17f3e131ab 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -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>, +) -> &'tcx ty::List> { + 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()), + ) + }) +} diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 7ffcf7b5d9654..d3c9514764991 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -368,3 +368,54 @@ impl> Iterator for FilterToTraits( + cx: I, + assumptions: impl IntoIterator>, +) -> HashSet> { + 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 +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index dd3cf1fc1811f..dfd683ac24399 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -145,6 +145,13 @@ pub trait Interner: type BoundRegion: BoundVarLike; type PlaceholderRegion: PlaceholderLike; + type RegionAssumptions: Copy + + Debug + + Hash + + Eq + + SliceLike> + + TypeFoldable; + // Predicates type ParamEnv: ParamEnv; type Predicate: Predicate; diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index db6fbefcf05cc..7c66542475050 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1150,4 +1150,5 @@ pub struct FnHeader { #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineWitnessTypes { pub types: I::Tys, + pub assumptions: I::RegionAssumptions, } diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 8b57db23d01c1..77414bec82d35 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -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 diff --git a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.stderr b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr similarity index 88% rename from tests/ui/async-await/drop-tracking-unresolved-typeck-results.stderr rename to tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr index 0d3ee8a93776b..d5560bf892032 100644 --- a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.stderr +++ b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr @@ -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 diff --git a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs index e6c295405e2b6..16f929331cb4c 100644 --- a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs +++ b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs @@ -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 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 }); } diff --git a/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr new file mode 100644 index 0000000000000..b298a3bf2153a --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr @@ -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`. diff --git a/tests/ui/async-await/higher-ranked-auto-trait-1.rs b/tests/ui/async-await/higher-ranked-auto-trait-1.rs new file mode 100644 index 0000000000000..740f7e2924545 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-1.rs @@ -0,0 +1,48 @@ +// Repro for . +//@ 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 { + pending_item: St::Item, +} + +fn filter(_: St) -> Filter { + unimplemented!(); +} + +struct FilterMap { + f: F, + pending: PhantomData, +} + +impl Stream for FilterMap +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() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr new file mode 100644 index 0000000000000..6fcf1b1eac176 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr new file mode 100644 index 0000000000000..6fcf1b1eac176 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.rs b/tests/ui/async-await/higher-ranked-auto-trait-10.rs new file mode 100644 index 0000000000000..4bfa27961abd0 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-10.rs @@ -0,0 +1,35 @@ +// Repro for . +//@ 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; +} + +impl<'a> Foo<'a> for &'a str { + type Error = (); + + fn foo(x: &'a str) -> Result { + Ok(x) + } +} + +async fn get_foo<'a, T>(x: &'a str) -> Result>::Error> +where + T: Foo<'a>, +{ + Foo::foo(x) +} + +fn bar<'a>(x: &'a str) -> Box> + Send + 'a> { + Box::new(async move { get_foo(x).await }) +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-11.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-11.assumptions.stderr new file mode 100644 index 0000000000000..d39843f628c46 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-11.assumptions.stderr @@ -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 + | -- lifetime `'a` defined here +... +LL | Box::pin(async move { >::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 { >::foo().await }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough + | + = note: `Send` would have to be implemented for the type `>::Future`, for any lifetime `'0`... + = note: ...but `Send` is actually implemented for the type `>::Future`, for some specific lifetime `'1` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-11.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-11.no_assumptions.stderr new file mode 100644 index 0000000000000..d39843f628c46 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-11.no_assumptions.stderr @@ -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 + | -- lifetime `'a` defined here +... +LL | Box::pin(async move { >::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 { >::foo().await }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough + | + = note: `Send` would have to be implemented for the type `>::Future`, for any lifetime `'0`... + = note: ...but `Send` is actually implemented for the type `>::Future`, for some specific lifetime `'1` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-11.rs b/tests/ui/async-await/higher-ranked-auto-trait-11.rs new file mode 100644 index 0000000000000..3aebdd8224da6 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-11.rs @@ -0,0 +1,31 @@ +// Repro for . +//@ 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; + + fn foo() -> Self::Future; +} + +struct MyType(T); + +impl<'a, T> Foo<'a> for MyType +where + T: Foo<'a>, + T::Future: Send, +{ + type Future = Pin + Send + 'a>>; + + fn foo() -> Self::Future { + Box::pin(async move { >::foo().await }) + } +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-12.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-12.no_assumptions.stderr new file mode 100644 index 0000000000000..63e71cbc40c75 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-12.no_assumptions.stderr @@ -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 + Send + '0)>` must implement `Robot`, for any lifetime `'0`... + = note: ...but `Robot` is actually implemented for the type `Box<(dyn Robot + Send + 'static)>` + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-12.rs b/tests/ui/async-await/higher-ranked-auto-trait-12.rs new file mode 100644 index 0000000000000..b1cca5cbb0054 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-12.rs @@ -0,0 +1,40 @@ +// Repro for . +//@ 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 + Send>; + +impl Robot for DynRobot { + type Id = u32; +} + +struct IRobot { + id: R::Id, + robot: R, +} + +// stand-in for tokio::spawn +fn this_is_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() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr new file mode 100644 index 0000000000000..f69218740dcca --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr new file mode 100644 index 0000000000000..cfbdaa8ad4beb --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.rs b/tests/ui/async-await/higher-ranked-auto-trait-13.rs new file mode 100644 index 0000000000000..4bce0f5197f8b --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.rs @@ -0,0 +1,68 @@ +// Repro for . +//@ 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, +} + +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]) { + ::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, +} + +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) { + let _named = + List::<'_, GetterImpl>> { 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) { + assert_send(my_send_async_method(struct_with_lifetime, data)); +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-14.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-14.no_assumptions.stderr new file mode 100644 index 0000000000000..b47db2b19ffe2 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-14.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-14.rs b/tests/ui/async-await/higher-ranked-auto-trait-14.rs new file mode 100644 index 0000000000000..5ed12cd6e38cb --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-14.rs @@ -0,0 +1,29 @@ +// Repro for . +//@ 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) -> impl Future + Send; +} + +fn foo( + val: T, + unique_x: HashSet, +) -> impl Future + 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() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr new file mode 100644 index 0000000000000..b4f3570d9f2da --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr @@ -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) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec,)>`, for any two lifetimes `'0` and `'1`... + = note: ...but it actually implements `FnOnce<(&Vec,)>` + +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) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec,)>`, for any two lifetimes `'0` and `'1`... + = note: ...but it actually implements `FnOnce<(&Vec,)>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-15.rs b/tests/ui/async-await/higher-ranked-auto-trait-15.rs new file mode 100644 index 0000000000000..153fcac4c3ae0 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-15.rs @@ -0,0 +1,21 @@ +// Repro for . +//@ 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![]; + for _ in things.iter().map(|n| n.iter()).flatten() { + // comment this line and everything compiles + async {}.await; + } +} + +fn require_send(_x: T) {} + +fn main() { + let future = listen(); + require_send(future); +} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-16.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-16.assumptions.stderr new file mode 100644 index 0000000000000..412c31b1bd843 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-16.assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-16.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-16.no_assumptions.stderr new file mode 100644 index 0000000000000..412c31b1bd843 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-16.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-16.rs b/tests/ui/async-await/higher-ranked-auto-trait-16.rs new file mode 100644 index 0000000000000..2b206f0a4c573 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-16.rs @@ -0,0 +1,23 @@ +// Repro for . +//@ 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) {} + +#[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() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr new file mode 100644 index 0000000000000..152900ca1ae9e --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-17.rs b/tests/ui/async-await/higher-ranked-auto-trait-17.rs new file mode 100644 index 0000000000000..152432466c005 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-17.rs @@ -0,0 +1,30 @@ +// Repro for . +//@ 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 + Send { + async move { + let iter = Adaptor::new(a.iter().map(|_: &()| {})); + std::future::pending::<()>().await; + drop(iter); + } +} + +struct Adaptor { + iter: T, + v: T::Item, +} + +impl Adaptor { + pub fn new(_: T) -> Self { + Self { iter: todo!(), v: todo!() } + } +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-2.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-2.no_assumptions.stderr new file mode 100644 index 0000000000000..2fc44412b9dae --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-2.no_assumptions.stderr @@ -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 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 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 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 for more information) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-2.rs b/tests/ui/async-await/higher-ranked-auto-trait-2.rs new file mode 100644 index 0000000000000..6c75597265bc2 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-2.rs @@ -0,0 +1,23 @@ +// Repro for . +//@ 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 + 'a + Send; +} + +pub trait FooExt: Foo { + fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future + 'a + Send { + async move { + // asks for an unspecified lifetime to outlive itself? weird diagnostics + self.run(t).await; + } + } +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-3.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-3.no_assumptions.stderr new file mode 100644 index 0000000000000..c5c98ac807ea9 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-3.no_assumptions.stderr @@ -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 for more information) + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-3.rs b/tests/ui/async-await/higher-ranked-auto-trait-3.rs new file mode 100644 index 0000000000000..d42d423668051 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-3.rs @@ -0,0 +1,72 @@ +// Repro for . +//@ 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 + 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 + Send + 'cx; + type Future2<'cx>: Future + 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 { + fi_1: FI1, + fi_2: FI2, +} + +impl IterCaller for UseIter +where + FI1: FutureIterator + 'static + Send + Sync, + for<'s, 'cx> FI1::Future<'s, 'cx>: Send, + FI2: FutureIterator + 'static + Send + Sync, +{ + type Future1<'cx> = impl Future + Send + 'cx + where + Self: 'cx; + + type Future2<'cx> = impl Future + 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() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-4.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-4.no_assumptions.stderr new file mode 100644 index 0000000000000..5aa5b83655a53 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-4.no_assumptions.stderr @@ -0,0 +1,10 @@ +error: higher-ranked lifetime error + --> $DIR/higher-ranked-auto-trait-4.rs:29:5 + | +LL | / async { +LL | | let _ = evil_function::().await; +LL | | } + | |_____^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-4.rs b/tests/ui/async-await/higher-ranked-auto-trait-4.rs new file mode 100644 index 0000000000000..0586af41e9e25 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-4.rs @@ -0,0 +1,34 @@ +// Repro for . +//@ 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 { + type Assoc; +} + +impl TraitWithAssocType<()> for T +where + T: ?Sized + 'static, +{ + type Assoc = (); +} + +fn evil_function + ?Sized, I>() +-> impl Future> { + async { Ok(()) } +} + +fn fails_to_compile() -> impl std::future::Future + Send { + async { + let _ = evil_function::().await; + } +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-5.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-5.no_assumptions.stderr new file mode 100644 index 0000000000000..8fa3c7483c89d --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-5.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-5.rs b/tests/ui/async-await/higher-ranked-auto-trait-5.rs new file mode 100644 index 0000000000000..9a8b3f4357c05 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-5.rs @@ -0,0 +1,54 @@ +// Repro for . +//@ 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(_future: F) +where + F: Future + Send, +{ +} + +pub trait CallMe { + fn call(&self) -> impl Future + Send; +} + +struct Wrap(T); + +impl CallMe for Wrap +where + S: CallMe + Send, +{ + // adding `+ Send` to this RPIT fixes the issue + fn call(&self) -> impl Future { + self.0.call() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct CallMeImpl { + value: T, +} + +impl CallMe for CallMeImpl +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 { + async {} + } +} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr new file mode 100644 index 0000000000000..d1f2d9a07530d --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr @@ -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`. diff --git a/tests/ui/async-await/higher-ranked-auto-trait-6.rs b/tests/ui/async-await/higher-ranked-auto-trait-6.rs new file mode 100644 index 0000000000000..2c6adf8938df4 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-6.rs @@ -0,0 +1,59 @@ +// Repro for . +//@ 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 + Send + 'a> { + Box::new(async { new(|| async { f().await }).await }) +} + +fn new(_a: A) -> F +where + A: Fn() -> B, +{ + F { _i: PhantomData } +} + +trait Stream { + type Item; +} + +struct T { + _a: PhantomData, + _b: PhantomData, +} + +impl Stream for T +where + A: Fn() -> B, +{ + type Item = B; +} + +struct F +where + A: Fn() -> B, +{ + _i: PhantomData< as Stream>::Item>, +} + +impl Future for F +where + A: Fn() -> B, +{ + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll { + unimplemented!() + } +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-7.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-7.no_assumptions.stderr new file mode 100644 index 0000000000000..dcb8075566e6d --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-7.no_assumptions.stderr @@ -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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-7.rs b/tests/ui/async-await/higher-ranked-auto-trait-7.rs new file mode 100644 index 0000000000000..abded5a88d3ae --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-7.rs @@ -0,0 +1,33 @@ +// Repro for . +//@ 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 + Send, +{ + f +} + +fn foo<'a, S: Trait + 'a>() { + future::<'a, S, _>(async move { + let result: PhantomData> = PhantomData; + async {}.await; + drop(result); + }); +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-8.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-8.no_assumptions.stderr new file mode 100644 index 0000000000000..6208675117b74 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-8.no_assumptions.stderr @@ -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: Send` + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-8.rs b/tests/ui/async-await/higher-ranked-auto-trait-8.rs new file mode 100644 index 0000000000000..91cef204e44b9 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-8.rs @@ -0,0 +1,27 @@ +// Repro for . +//@ edition: 2021 +//@ revisions: assumptions no_assumptions +//@[assumptions] compile-flags: -Zhigher-ranked-assumptions +//@[assumptions] check-pass +//@[no_assumptions] known-bug: #110338 + +fn needs_send(_val: T) {} +async fn use_async(_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 +} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-9.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-9.no_assumptions.stderr new file mode 100644 index 0000000000000..809cbcf0cad13 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-9.no_assumptions.stderr @@ -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 + 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 + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-9.rs b/tests/ui/async-await/higher-ranked-auto-trait-9.rs new file mode 100644 index 0000000000000..66edbf23f2b21 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-9.rs @@ -0,0 +1,44 @@ +// Repro for . +//@ 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 Foo for F +where + Self: FnOnce() -> I, + I: fmt::Debug, +{ + type Item = I; +} + +async fn foo_item(_: F) -> F::Item { + unimplemented!() +} + +fn main() { + let fut = async { + let callback = || -> Box { unimplemented!() }; + + // Using plain fn instead of a closure fixes the error, + // though you obviously can't capture any state... + // fn callback() -> Box { + // todo!() + // } + + foo_item(callback).await; + }; + + // Removing `+ Send` bound also fixes the error, + // though at the cost of loosing `Send`ability... + let fut: &(dyn Future + Send) = &fut as _; +} diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr b/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr similarity index 94% rename from tests/ui/async-await/return-type-notation/issue-110963-early.stderr rename to tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr index d6c3bd12aee3b..bb43636492409 100644 --- a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr +++ b/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr @@ -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 { ::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; diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.rs b/tests/ui/async-await/return-type-notation/issue-110963-early.rs index 46b8fbf6f86e3..8c3180c011910 100644 --- a/tests/ui/async-await/return-type-notation/issue-110963-early.rs +++ b/tests/ui/async-await/return-type-notation/issue-110963-early.rs @@ -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)] diff --git a/tests/ui/generic-associated-types/bugs/issue-100013.stderr b/tests/ui/generic-associated-types/bugs/issue-100013.stderr deleted file mode 100644 index ff82aebfef911..0000000000000 --- a/tests/ui/generic-associated-types/bugs/issue-100013.stderr +++ /dev/null @@ -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::>; // 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 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::>; // 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 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::>; // 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::>; // 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 for more information) - -error: aborting due to 4 previous errors - diff --git a/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.no_assumptions.stderr b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.no_assumptions.stderr new file mode 100644 index 0000000000000..8b62fb6a25473 --- /dev/null +++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.no_assumptions.stderr @@ -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::>; // 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 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::>; // 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 for more information) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/generic-associated-types/bugs/issue-100013.rs b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.rs similarity index 54% rename from tests/ui/generic-associated-types/bugs/issue-100013.rs rename to tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.rs index ac72c29c03b92..a5ed162d62c90 100644 --- a/tests/ui/generic-associated-types/bugs/issue-100013.rs +++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.rs @@ -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() -> impl Send { } } -fn call2<'a, 'b, I: FutureIterator>() -> impl Send { - async { // a coroutine checked for autotrait impl `Send` - let x = None::>; // 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::>; // a type referencing GAT async {}.await; // a yield point diff --git a/tests/ui/generic-associated-types/issue-92096.stderr b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.no_assumptions.stderr similarity index 74% rename from tests/ui/generic-associated-types/issue-92096.stderr rename to tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.no_assumptions.stderr index b9a16cf184e4c..f747ba9a73341 100644 --- a/tests/ui/generic-associated-types/issue-92096.stderr +++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.no_assumptions.stderr @@ -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 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.rs b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.rs new file mode 100644 index 0000000000000..5f683bd82fa46 --- /dev/null +++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.rs @@ -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) -> impl '_ + Future + Send +where + C: Client + Send + Sync, +{ + async move { c.connect().await } +} + +fn main() {} diff --git a/tests/ui/generic-associated-types/issue-92096.rs b/tests/ui/generic-associated-types/issue-92096.rs deleted file mode 100644 index a34c41795849e..0000000000000 --- a/tests/ui/generic-associated-types/issue-92096.rs +++ /dev/null @@ -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) -> 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::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() {}