Consider outlives assumptions when proving auto traits for coroutine interiors
This commit is contained in:
@@ -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>>>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user