Consider outlives assumptions when proving auto traits for coroutine interiors
This commit is contained in:
@@ -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,7 +115,11 @@ 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;
|
||||
}
|
||||
@@ -121,6 +128,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
||||
&mut self,
|
||||
predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
|
||||
constraint_category: ConstraintCategory<'tcx>,
|
||||
higher_ranked_assumptions: &FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
debug!("generate: constraints at: {:#?}", self.locations);
|
||||
@@ -150,7 +158,13 @@ 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 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);
|
||||
@@ -273,7 +287,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
||||
match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
|
||||
{
|
||||
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
|
||||
if let Some(QueryRegionConstraints { outlives }) = constraints {
|
||||
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
|
||||
if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
|
||||
next_outlives_predicates.extend(outlives.iter().copied());
|
||||
}
|
||||
ty
|
||||
|
||||
@@ -131,6 +131,11 @@ pub(crate) fn type_check<'tcx>(
|
||||
pre_obligations.is_empty(),
|
||||
"there should be no incoming region obligations = {pre_obligations:#?}",
|
||||
);
|
||||
let pre_assumptions = infcx.take_registered_region_assumptions();
|
||||
assert!(
|
||||
pre_assumptions.is_empty(),
|
||||
"there should be no incoming region assumptions = {pre_assumptions:#?}",
|
||||
);
|
||||
|
||||
debug!(?normalized_inputs_and_output);
|
||||
|
||||
|
||||
@@ -116,9 +116,15 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
|
||||
let region_obligations = self.take_registered_region_obligations();
|
||||
let region_assumptions = self.take_registered_region_assumptions();
|
||||
debug!(?region_obligations);
|
||||
let region_constraints = self.with_region_constraints(|region_constraints| {
|
||||
make_query_region_constraints(tcx, region_obligations, region_constraints)
|
||||
make_query_region_constraints(
|
||||
tcx,
|
||||
region_obligations,
|
||||
region_constraints,
|
||||
region_assumptions,
|
||||
)
|
||||
});
|
||||
debug!(?region_constraints);
|
||||
|
||||
@@ -169,6 +175,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
self.register_outlives_constraint(predicate, cause);
|
||||
}
|
||||
|
||||
for assumption in &query_response.value.region_constraints.assumptions {
|
||||
let assumption = instantiate_value(self.tcx, &result_args, *assumption);
|
||||
self.register_region_assumption(assumption);
|
||||
}
|
||||
|
||||
let user_result: R =
|
||||
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
|
||||
|
||||
@@ -292,6 +303,18 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}),
|
||||
);
|
||||
|
||||
// FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions
|
||||
// at once, rather than calling `instantiate_value` repeatedly which may
|
||||
// create more universes.
|
||||
output_query_region_constraints.assumptions.extend(
|
||||
query_response
|
||||
.value
|
||||
.region_constraints
|
||||
.assumptions
|
||||
.iter()
|
||||
.map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)),
|
||||
);
|
||||
|
||||
let user_result: R =
|
||||
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
|
||||
|
||||
@@ -567,6 +590,7 @@ pub fn make_query_region_constraints<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
outlives_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
|
||||
region_constraints: &RegionConstraintData<'tcx>,
|
||||
assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
) -> QueryRegionConstraints<'tcx> {
|
||||
let RegionConstraintData { constraints, verifys } = region_constraints;
|
||||
|
||||
@@ -602,5 +626,5 @@ pub fn make_query_region_constraints<'tcx>(
|
||||
}))
|
||||
.collect();
|
||||
|
||||
QueryRegionConstraints { outlives }
|
||||
QueryRegionConstraints { outlives, assumptions }
|
||||
}
|
||||
|
||||
@@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> {
|
||||
/// that all type inference variables have been bound and so forth.
|
||||
region_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
|
||||
|
||||
/// The outlives bounds that we assume must hold about placeholders that
|
||||
/// come from instantiating the binder of coroutine-witnesses. These bounds
|
||||
/// are deduced from the well-formedness of the witness's types, and are
|
||||
/// necessary because of the way we anonymize the regions in a coroutine,
|
||||
/// which may cause types to no longer be considered well-formed.
|
||||
region_assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
|
||||
/// Caches for opaque type inference.
|
||||
opaque_type_storage: OpaqueTypeStorage<'tcx>,
|
||||
}
|
||||
@@ -164,7 +171,8 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
||||
int_unification_storage: Default::default(),
|
||||
float_unification_storage: Default::default(),
|
||||
region_constraint_storage: Some(Default::default()),
|
||||
region_obligations: vec![],
|
||||
region_obligations: Default::default(),
|
||||
region_assumptions: Default::default(),
|
||||
opaque_type_storage: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -174,6 +182,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
||||
&self.region_obligations
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn region_assumptions(&self) -> &[ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>] {
|
||||
&self.region_assumptions
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> {
|
||||
self.projection_cache.with_log(&mut self.undo_log)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::transitive_relation::TransitiveRelationBuilder;
|
||||
use rustc_middle::{bug, ty};
|
||||
use tracing::debug;
|
||||
@@ -39,6 +39,9 @@ pub struct OutlivesEnvironment<'tcx> {
|
||||
/// optimized in the future, though.
|
||||
region_bound_pairs: RegionBoundPairs<'tcx>,
|
||||
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
|
||||
/// Assumptions that come from the well-formedness of coroutines that we prove
|
||||
/// auto trait bounds for during the type checking of this body.
|
||||
higher_ranked_assumptions: FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
}
|
||||
|
||||
/// "Region-bound pairs" tracks outlives relations that are known to
|
||||
@@ -52,6 +55,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
|
||||
extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
|
||||
higher_ranked_assumptions: FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
) -> Self {
|
||||
let mut region_relation = TransitiveRelationBuilder::default();
|
||||
let mut region_bound_pairs = RegionBoundPairs::default();
|
||||
@@ -88,6 +92,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
known_type_outlives,
|
||||
free_region_map: FreeRegionMap { relation: region_relation.freeze() },
|
||||
region_bound_pairs,
|
||||
higher_ranked_assumptions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,4 +107,10 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
|
||||
&self.known_type_outlives
|
||||
}
|
||||
|
||||
pub fn higher_ranked_assumptions(
|
||||
&self,
|
||||
) -> &FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
|
||||
&self.higher_ranked_assumptions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use super::region_constraints::{RegionConstraintData, UndoLog};
|
||||
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
|
||||
use crate::infer::free_regions::RegionRelations;
|
||||
use crate::infer::lexical_region_resolve;
|
||||
use crate::infer::region_constraints::Constraint;
|
||||
|
||||
pub mod env;
|
||||
pub mod for_liveness;
|
||||
@@ -54,18 +55,27 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let storage = {
|
||||
let mut storage = {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let inner = &mut *inner;
|
||||
assert!(
|
||||
self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(),
|
||||
"region_obligations not empty: {:#?}",
|
||||
inner.region_obligations
|
||||
inner.region_obligations,
|
||||
);
|
||||
assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
|
||||
inner.region_constraint_storage.take().expect("regions already resolved")
|
||||
};
|
||||
|
||||
// Filter out any region-region outlives assumptions that are implied by
|
||||
// coroutine well-formedness.
|
||||
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 +103,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()
|
||||
}
|
||||
|
||||
@@ -170,6 +170,21 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
std::mem::take(&mut self.inner.borrow_mut().region_obligations)
|
||||
}
|
||||
|
||||
pub fn register_region_assumption(
|
||||
&self,
|
||||
assumption: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
|
||||
) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.undo_log.push(UndoLog::PushRegionAssumption);
|
||||
inner.region_assumptions.push(assumption);
|
||||
}
|
||||
|
||||
pub fn take_registered_region_assumptions(
|
||||
&self,
|
||||
) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
|
||||
std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
|
||||
}
|
||||
|
||||
/// Process the region obligations that must be proven (during
|
||||
/// `regionck`) for the given `body_id`, given information about
|
||||
/// the region bounds in scope and so forth.
|
||||
@@ -220,6 +235,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
let (sup_type, sub_region) =
|
||||
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
|
||||
|
||||
if outlives_env
|
||||
.higher_ranked_assumptions()
|
||||
.contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!(?sup_type, ?sub_region, ?origin);
|
||||
|
||||
let outlives = &mut TypeOutlives::new(
|
||||
|
||||
@@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> {
|
||||
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
|
||||
ProjectionCache(traits::UndoLog<'tcx>),
|
||||
PushTypeOutlivesConstraint,
|
||||
PushRegionAssumption,
|
||||
}
|
||||
|
||||
macro_rules! impl_from {
|
||||
@@ -77,6 +78,9 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
|
||||
let popped = self.region_obligations.pop();
|
||||
assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
|
||||
}
|
||||
UndoLog::PushRegionAssumption => {
|
||||
self.region_assumptions.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> {
|
||||
#[derive(HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct QueryRegionConstraints<'tcx> {
|
||||
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
|
||||
pub assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
}
|
||||
|
||||
impl QueryRegionConstraints<'_> {
|
||||
/// Represents an empty (trivially true) set of region
|
||||
/// constraints.
|
||||
/// Represents an empty (trivially true) set of region constraints.
|
||||
///
|
||||
/// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions?
|
||||
/// Because I don't expect for us to get cases where an assumption from one query would
|
||||
/// discharge a requirement from another query, which is a potential problem if we did throw
|
||||
/// away these assumptions because there were no constraints.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.outlives.is_empty()
|
||||
self.outlives.is_empty() && self.assumptions.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use rustc_infer::infer::{InferCtxt, RegionResolutionError};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, elaborate};
|
||||
|
||||
use crate::traits::ScrubbedTraitError;
|
||||
use crate::traits::outlives_bounds::InferCtxtExt;
|
||||
@@ -46,6 +46,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(-Znext-trait-solver): Normalize these.
|
||||
let higher_ranked_assumptions = infcx.take_registered_region_assumptions();
|
||||
let higher_ranked_assumptions =
|
||||
elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions);
|
||||
|
||||
// FIXME: This needs to be modified so that we normalize the known type
|
||||
// outlives obligations then elaborate them into their region/type components.
|
||||
// Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
|
||||
@@ -59,6 +64,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
assumed_wf_tys,
|
||||
disable_implied_bounds_hack,
|
||||
),
|
||||
higher_ranked_assumptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +212,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
||||
// Cannot use `take_registered_region_obligations` as we may compute the response
|
||||
// inside of a `probe` whenever we have multiple choices inside of the solver.
|
||||
let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
|
||||
let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
|
||||
let region_constraints = self.0.with_region_constraints(|region_constraints| {
|
||||
make_query_region_constraints(self.tcx, region_obligations, region_constraints)
|
||||
make_query_region_constraints(
|
||||
self.tcx,
|
||||
region_obligations,
|
||||
region_constraints,
|
||||
region_assumptions,
|
||||
)
|
||||
});
|
||||
|
||||
let mut seen = FxHashSet::default();
|
||||
|
||||
@@ -461,6 +461,7 @@ fn impl_intersection_has_negative_obligation(
|
||||
// requirements, when proving the negated where clauses below.
|
||||
drop(equate_obligations);
|
||||
drop(infcx.take_registered_region_obligations());
|
||||
drop(infcx.take_registered_region_assumptions());
|
||||
drop(infcx.take_and_reset_region_constraints());
|
||||
|
||||
plug_infer_with_placeholders(
|
||||
|
||||
@@ -78,7 +78,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
|
||||
bounds.retain(|bound| !bound.has_placeholders());
|
||||
|
||||
if !constraints.is_empty() {
|
||||
let QueryRegionConstraints { outlives } = constraints;
|
||||
// FIXME(higher_ranked_auto): Should we register assumptions here?
|
||||
// We otherwise would get spurious errors if normalizing an implied
|
||||
// outlives bound required proving some higher-ranked coroutine obl.
|
||||
let QueryRegionConstraints { outlives, assumptions: _ } = constraints;
|
||||
let cause = ObligationCause::misc(span, body_id);
|
||||
for &(predicate, _) in &outlives {
|
||||
infcx.register_outlives_constraint(predicate, &cause);
|
||||
|
||||
@@ -80,6 +80,11 @@ where
|
||||
pre_obligations.is_empty(),
|
||||
"scrape_region_constraints: incoming region obligations = {pre_obligations:#?}",
|
||||
);
|
||||
let pre_assumptions = infcx.take_registered_region_assumptions();
|
||||
assert!(
|
||||
pre_assumptions.is_empty(),
|
||||
"scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}",
|
||||
);
|
||||
|
||||
let value = infcx.commit_if_ok(|_| {
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
@@ -100,11 +105,13 @@ where
|
||||
let value = infcx.resolve_vars_if_possible(value);
|
||||
|
||||
let region_obligations = infcx.take_registered_region_obligations();
|
||||
let region_assumptions = infcx.take_registered_region_assumptions();
|
||||
let region_constraint_data = infcx.take_and_reset_region_constraints();
|
||||
let region_constraints = query_response::make_query_region_constraints(
|
||||
infcx.tcx,
|
||||
region_obligations,
|
||||
®ion_constraint_data,
|
||||
region_assumptions,
|
||||
);
|
||||
|
||||
if region_constraints.is_empty() {
|
||||
|
||||
@@ -180,8 +180,9 @@ where
|
||||
span,
|
||||
)?;
|
||||
output.error_info = error_info;
|
||||
if let Some(QueryRegionConstraints { outlives }) = output.constraints {
|
||||
if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints {
|
||||
region_constraints.outlives.extend(outlives.iter().cloned());
|
||||
region_constraints.assumptions.extend(assumptions.iter().cloned());
|
||||
}
|
||||
output.constraints = if region_constraints.is_empty() {
|
||||
None
|
||||
|
||||
@@ -411,18 +411,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
|
||||
let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty);
|
||||
|
||||
let types = self.constituent_types_for_ty(self_ty)?;
|
||||
let types = self.infcx.enter_forall_and_leak_universe(types);
|
||||
let constituents = self.constituent_types_for_auto_trait(self_ty)?;
|
||||
let constituents = self.infcx.enter_forall_and_leak_universe(constituents);
|
||||
|
||||
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
|
||||
let obligations = self.collect_predicates_for_types(
|
||||
let mut obligations = self.collect_predicates_for_types(
|
||||
obligation.param_env,
|
||||
cause,
|
||||
cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.predicate.def_id(),
|
||||
types,
|
||||
constituents.types,
|
||||
);
|
||||
|
||||
// FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
|
||||
// and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
|
||||
// matter yet.
|
||||
for assumption in constituents.assumptions {
|
||||
let assumption = normalize_with_depth_to(
|
||||
self,
|
||||
obligation.param_env,
|
||||
cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
assumption,
|
||||
&mut obligations,
|
||||
);
|
||||
self.infcx.register_region_assumption(assumption);
|
||||
}
|
||||
|
||||
Ok(obligations)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::infer::relate::TypeRelation;
|
||||
use rustc_infer::traits::{PredicateObligations, TraitObligation};
|
||||
use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
|
||||
pub use rustc_middle::traits::select::*;
|
||||
@@ -2247,10 +2248,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
/// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
|
||||
/// ```
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn constituent_types_for_ty(
|
||||
fn constituent_types_for_auto_trait(
|
||||
&self,
|
||||
t: Ty<'tcx>,
|
||||
) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
|
||||
) -> Result<ty::Binder<'tcx, AutoImplConstituents<'tcx>>, SelectionError<'tcx>> {
|
||||
Ok(match *t.kind() {
|
||||
ty::Uint(_)
|
||||
| ty::Int(_)
|
||||
@@ -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<T>`, we pass `T`.
|
||||
ty::Adt(def, args) if def.is_phantom_data() => {
|
||||
ty::Binder::dummy(args.types().collect())
|
||||
ty::Binder::dummy(AutoImplConstituents {
|
||||
types: args.types().collect(),
|
||||
assumptions: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
ty::Adt(def, args) => {
|
||||
ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect())
|
||||
}
|
||||
ty::Adt(def, args) => ty::Binder::dummy(AutoImplConstituents {
|
||||
types: def.all_fields().map(|f| f.ty(self.tcx(), args)).collect(),
|
||||
assumptions: vec![],
|
||||
}),
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
if self.infcx.can_define_opaque_ty(def_id) {
|
||||
@@ -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<Ty<'tcx>>,
|
||||
pub assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
|
||||
}
|
||||
|
||||
@@ -66,12 +66,14 @@ fn compute_assumptions<'tcx>(
|
||||
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));
|
||||
|
||||
@@ -368,3 +368,54 @@ impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I,
|
||||
(0, upper)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elaborate_outlives_assumptions<I: Interner>(
|
||||
cx: I,
|
||||
assumptions: impl IntoIterator<Item = ty::OutlivesPredicate<I, I::GenericArg>>,
|
||||
) -> HashSet<ty::OutlivesPredicate<I, I::GenericArg>> {
|
||||
let mut collected = HashSet::default();
|
||||
|
||||
for ty::OutlivesPredicate(arg1, r2) in assumptions {
|
||||
collected.insert(ty::OutlivesPredicate(arg1, r2));
|
||||
match arg1.kind() {
|
||||
// Elaborate the components of an type, since we may have substituted a
|
||||
// generic coroutine with a more specific type.
|
||||
ty::GenericArgKind::Type(ty1) => {
|
||||
let mut components = smallvec![];
|
||||
push_outlives_components(cx, ty1, &mut components);
|
||||
for c in components {
|
||||
match c {
|
||||
Component::Region(r1) => {
|
||||
if !r1.is_bound() {
|
||||
collected.insert(ty::OutlivesPredicate(r1.into(), r2));
|
||||
}
|
||||
}
|
||||
|
||||
Component::Param(p) => {
|
||||
let ty = Ty::new_param(cx, p);
|
||||
collected.insert(ty::OutlivesPredicate(ty.into(), r2));
|
||||
}
|
||||
|
||||
Component::Placeholder(p) => {
|
||||
let ty = Ty::new_placeholder(cx, p);
|
||||
collected.insert(ty::OutlivesPredicate(ty.into(), r2));
|
||||
}
|
||||
|
||||
Component::Alias(alias_ty) => {
|
||||
collected.insert(ty::OutlivesPredicate(alias_ty.to_ty(cx).into(), r2));
|
||||
}
|
||||
|
||||
Component::UnresolvedInferenceVariable(_) | Component::EscapingAlias(_) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nothing to elaborate for a region.
|
||||
ty::GenericArgKind::Lifetime(_) => {}
|
||||
// Consts don't really participate in outlives.
|
||||
ty::GenericArgKind::Const(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
collected
|
||||
}
|
||||
|
||||
@@ -1021,7 +1021,6 @@ ui/foreign/issue-91370-foreign-fn-block-impl.rs
|
||||
ui/foreign/issue-99276-same-type-lifetimes.rs
|
||||
ui/function-pointer/issue-102289.rs
|
||||
ui/functions-closures/closure-expected-type/issue-38714.rs
|
||||
ui/generic-associated-types/bugs/issue-100013.rs
|
||||
ui/generic-associated-types/bugs/issue-80626.rs
|
||||
ui/generic-associated-types/bugs/issue-87735.rs
|
||||
ui/generic-associated-types/bugs/issue-87755.rs
|
||||
@@ -1099,7 +1098,6 @@ ui/generic-associated-types/issue-90729.rs
|
||||
ui/generic-associated-types/issue-91139.rs
|
||||
ui/generic-associated-types/issue-91883.rs
|
||||
ui/generic-associated-types/issue-92033.rs
|
||||
ui/generic-associated-types/issue-92096.rs
|
||||
ui/generic-associated-types/issue-92280.rs
|
||||
ui/generic-associated-types/issue-92954.rs
|
||||
ui/generic-associated-types/issue-93141.rs
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//@ incremental
|
||||
//@ edition: 2021
|
||||
//@ check-pass
|
||||
|
||||
use std::future::*;
|
||||
use std::marker::PhantomData;
|
||||
@@ -96,8 +97,6 @@ impl<St: ?Sized + Stream + Unpin> Future for Next<'_, St> {
|
||||
|
||||
fn main() {
|
||||
send(async {
|
||||
//~^ ERROR implementation of `FnOnce` is not general enough
|
||||
//~| ERROR implementation of `FnOnce` is not general enough
|
||||
Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/drop-tracking-unresolved-typeck-results.rs:98: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
|
||||
|
|
||||
= note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'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/drop-tracking-unresolved-typeck-results.rs:98: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
|
||||
|
|
||||
= note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//@ edition: 2021
|
||||
//@ known-bug: #110963
|
||||
//@ check-pass
|
||||
|
||||
#![feature(return_type_notation)]
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/issue-110963-early.rs:14:5
|
||||
|
|
||||
LL | / spawn(async move {
|
||||
LL | | let mut hc = hc;
|
||||
LL | | if !hc.check().await {
|
||||
LL | | log_health_check_failure().await;
|
||||
LL | | }
|
||||
LL | | });
|
||||
| |______^ implementation of `Send` is not general enough
|
||||
|
|
||||
= note: `Send` would have to be implemented for the type `impl Future<Output = bool> { <HC as HealthCheck>::check<'0>(..) }`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Send` is actually implemented for the type `impl Future<Output = bool> { <HC as HealthCheck>::check<'2>(..) }`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Send` is not general enough
|
||||
--> $DIR/issue-110963-early.rs:14:5
|
||||
|
|
||||
LL | / spawn(async move {
|
||||
LL | | let mut hc = hc;
|
||||
LL | | if !hc.check().await {
|
||||
LL | | log_health_check_failure().await;
|
||||
LL | | }
|
||||
LL | | });
|
||||
| |______^ implementation of `Send` is not general enough
|
||||
|
|
||||
= note: `Send` would have to be implemented for the type `impl Future<Output = bool> { <HC as HealthCheck>::check<'0>(..) }`, for any two lifetimes `'0` and `'1`...
|
||||
= note: ...but `Send` is actually implemented for the type `impl Future<Output = bool> { <HC as HealthCheck>::check<'2>(..) }`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/issue-100013.rs:15:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/issue-100013.rs:22:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/issue-100013.rs:23:17
|
||||
|
|
||||
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
|
||||
| -- -- lifetime `'b` defined here
|
||||
| |
|
||||
| lifetime `'a` defined here
|
||||
LL | async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'b`
|
||||
|
||||
error: lifetime bound not satisfied
|
||||
--> $DIR/issue-100013.rs:29:5
|
||||
|
|
||||
LL | / async { // a coroutine checked for autotrait impl `Send`
|
||||
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
LL | | async {}.await; // a yield point
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
//@ check-fail
|
||||
//@ known-bug: #100013
|
||||
//@ check-pass
|
||||
//@ edition: 2021
|
||||
|
||||
// We really should accept this, but we need implied bounds between the regions
|
||||
// in a coroutine interior.
|
||||
|
||||
pub trait FutureIterator {
|
||||
type Future<'s, 'cx>: Send
|
||||
where
|
||||
@@ -18,14 +14,7 @@ fn call<I: FutureIterator>() -> impl Send {
|
||||
}
|
||||
}
|
||||
|
||||
fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
|
||||
async { // a coroutine checked for autotrait impl `Send`
|
||||
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
async {}.await; // a yield point
|
||||
}
|
||||
}
|
||||
|
||||
fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
|
||||
fn call2<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
|
||||
async { // a coroutine checked for autotrait impl `Send`
|
||||
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
|
||||
async {}.await; // a yield point
|
||||
@@ -0,0 +1,21 @@
|
||||
//@ edition:2018
|
||||
//@ check-pass
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait Client {
|
||||
type Connecting<'a>: Future + Send
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn connect(&'_ self) -> Self::Connecting<'_>;
|
||||
}
|
||||
|
||||
fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
|
||||
where
|
||||
C: Client + Send + Sync,
|
||||
{
|
||||
async move { c.connect().await }
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,28 +0,0 @@
|
||||
//@ edition:2018
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait Client {
|
||||
type Connecting<'a>: Future + Send
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn connect(&'_ self) -> Self::Connecting<'_>;
|
||||
}
|
||||
|
||||
fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
|
||||
where
|
||||
C: Client + Send + Sync,
|
||||
{
|
||||
async move { c.connect().await }
|
||||
//~^ ERROR `C` does not live long enough
|
||||
//
|
||||
// FIXME(#71723). This is because we infer at some point a value of
|
||||
//
|
||||
// impl Future<Output = <C as Client>::Connection<'_>>
|
||||
//
|
||||
// and then we somehow fail the WF check because `where C: 'a` is not known,
|
||||
// but I'm not entirely sure how that comes about.
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,8 +0,0 @@
|
||||
error: `C` does not live long enough
|
||||
--> $DIR/issue-92096.rs:17:5
|
||||
|
|
||||
LL | async move { c.connect().await }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Reference in New Issue
Block a user