Make empty bounds lower to WellFormed and make WellFormed coinductive
This commit is contained in:
parent
8e430bfa9a
commit
e16dbb5076
8 changed files with 100 additions and 26 deletions
|
@ -145,6 +145,7 @@ where
|
||||||
}
|
}
|
||||||
ControlFlow::CONTINUE
|
ControlFlow::CONTINUE
|
||||||
}
|
}
|
||||||
|
ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
|
||||||
_ => bug!("unexpected predicate: {:?}", predicate),
|
_ => bug!("unexpected predicate: {:?}", predicate),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,20 +488,70 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::WellFormed(arg) => match wf::obligations(
|
ty::PredicateKind::WellFormed(arg) => {
|
||||||
self.infcx,
|
// So, there is a bit going on here. First, `WellFormed` predicates
|
||||||
obligation.param_env,
|
// are coinductive, like trait predicates with auto traits.
|
||||||
obligation.cause.body_id,
|
// This means that we need to detect if we have recursively
|
||||||
obligation.recursion_depth + 1,
|
// evaluated `WellFormed(X)`. Otherwise, we would run into
|
||||||
arg,
|
// a "natural" overflow error.
|
||||||
obligation.cause.span,
|
//
|
||||||
) {
|
// Now, the next question is whether we need to do anything
|
||||||
Some(mut obligations) => {
|
// special with caching. Considering the following tree:
|
||||||
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
|
// - `WF(Foo<T>)`
|
||||||
self.evaluate_predicates_recursively(previous_stack, obligations)
|
// - `Bar<T>: Send`
|
||||||
|
// - `WF(Foo<T>)`
|
||||||
|
// - `Foo<T>: Trait`
|
||||||
|
// In this case, the innermost `WF(Foo<T>)` should return
|
||||||
|
// `EvaluatedToOk`, since it's coinductive. Then if
|
||||||
|
// `Bar<T>: Send` is resolved to `EvaluatedToOk`, it can be
|
||||||
|
// inserted into a cache (because without thinking about `WF`
|
||||||
|
// goals, it isn't in a cycle). If `Foo<T>: Trait` later doesn't
|
||||||
|
// hold, then `Bar<T>: Send` shouldn't hold. Therefore, we
|
||||||
|
// *do* need to keep track of coinductive cycles.
|
||||||
|
|
||||||
|
let cache = previous_stack.cache;
|
||||||
|
let dfn = cache.next_dfn();
|
||||||
|
|
||||||
|
for stack_arg in previous_stack.cache.wf_tys.borrow().iter().rev() {
|
||||||
|
if stack_arg.0 != arg {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debug!("WellFormed({:?}) on stack", arg);
|
||||||
|
if let Some(stack) = previous_stack.head {
|
||||||
|
stack.update_reached_depth(stack_arg.1);
|
||||||
|
}
|
||||||
|
return Ok(EvaluatedToOk);
|
||||||
}
|
}
|
||||||
None => Ok(EvaluatedToAmbig),
|
|
||||||
},
|
match wf::obligations(
|
||||||
|
self.infcx,
|
||||||
|
obligation.param_env,
|
||||||
|
obligation.cause.body_id,
|
||||||
|
obligation.recursion_depth + 1,
|
||||||
|
arg,
|
||||||
|
obligation.cause.span,
|
||||||
|
) {
|
||||||
|
Some(mut obligations) => {
|
||||||
|
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
|
||||||
|
|
||||||
|
cache.wf_tys.borrow_mut().push((arg, previous_stack.depth()));
|
||||||
|
let result =
|
||||||
|
self.evaluate_predicates_recursively(previous_stack, obligations);
|
||||||
|
cache.wf_tys.borrow_mut().pop();
|
||||||
|
|
||||||
|
let result = result?;
|
||||||
|
|
||||||
|
if !result.must_apply_modulo_regions() {
|
||||||
|
cache.on_failure(dfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.on_completion(dfn);
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
None => Ok(EvaluatedToAmbig),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ty::PredicateKind::TypeOutlives(pred) => {
|
ty::PredicateKind::TypeOutlives(pred) => {
|
||||||
// A global type with no late-bound regions can only
|
// A global type with no late-bound regions can only
|
||||||
|
@ -718,6 +768,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
|
|
||||||
debug!(?fresh_trait_pred);
|
debug!(?fresh_trait_pred);
|
||||||
|
|
||||||
|
// If a trait predicate is in the (local or global) evaluation cache,
|
||||||
|
// then we know it holds without cycles.
|
||||||
if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {
|
if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {
|
||||||
debug!(?result, "CACHE HIT");
|
debug!(?result, "CACHE HIT");
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
|
@ -921,7 +973,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
/// - it also appears in the backtrace at some position `X`,
|
/// - it also appears in the backtrace at some position `X`,
|
||||||
/// - all the predicates at positions `X..` between `X` and the top are
|
/// - all the predicates at positions `X..` between `X` and the top are
|
||||||
/// also defaulted traits.
|
/// also defaulted traits.
|
||||||
pub fn coinductive_match<I>(&mut self, mut cycle: I) -> bool
|
pub(crate) fn coinductive_match<I>(&mut self, mut cycle: I) -> bool
|
||||||
where
|
where
|
||||||
I: Iterator<Item = ty::Predicate<'tcx>>,
|
I: Iterator<Item = ty::Predicate<'tcx>>,
|
||||||
{
|
{
|
||||||
|
@ -931,6 +983,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
|
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
|
||||||
let result = match predicate.kind().skip_binder() {
|
let result = match predicate.kind().skip_binder() {
|
||||||
ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()),
|
ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()),
|
||||||
|
ty::PredicateKind::WellFormed(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
debug!(?predicate, ?result, "coinductive_predicate");
|
debug!(?predicate, ?result, "coinductive_predicate");
|
||||||
|
@ -2411,6 +2464,8 @@ struct ProvisionalEvaluationCache<'tcx> {
|
||||||
/// all cache values whose DFN is >= 4 -- in this case, that
|
/// all cache values whose DFN is >= 4 -- in this case, that
|
||||||
/// means the cached value for `F`.
|
/// means the cached value for `F`.
|
||||||
map: RefCell<FxHashMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>,
|
map: RefCell<FxHashMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>,
|
||||||
|
|
||||||
|
wf_tys: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A cache value for the provisional cache: contains the depth-first
|
/// A cache value for the provisional cache: contains the depth-first
|
||||||
|
@ -2424,7 +2479,7 @@ struct ProvisionalEvaluation {
|
||||||
|
|
||||||
impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
|
impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { dfn: Cell::new(0), map: Default::default() }
|
Self { dfn: Cell::new(0), map: Default::default(), wf_tys: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,8 +106,16 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
|
||||||
ty::PredicateKind::Projection(predicate) => chalk_ir::DomainGoal::Holds(
|
ty::PredicateKind::Projection(predicate) => chalk_ir::DomainGoal::Holds(
|
||||||
chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
|
chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
|
||||||
),
|
),
|
||||||
ty::PredicateKind::WellFormed(..)
|
ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
|
||||||
| ty::PredicateKind::ObjectSafe(..)
|
ty::GenericArgKind::Type(ty) => chalk_ir::DomainGoal::WellFormed(
|
||||||
|
chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
|
||||||
|
),
|
||||||
|
// FIXME(chalk): we need to change `WellFormed` in Chalk to take a `GenericArg`
|
||||||
|
_ => chalk_ir::DomainGoal::WellFormed(chalk_ir::WellFormed::Ty(
|
||||||
|
interner.tcx.types.unit.lower_into(interner),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
ty::PredicateKind::ObjectSafe(..)
|
||||||
| ty::PredicateKind::ClosureKind(..)
|
| ty::PredicateKind::ClosureKind(..)
|
||||||
| ty::PredicateKind::Subtype(..)
|
| ty::PredicateKind::Subtype(..)
|
||||||
| ty::PredicateKind::Coerce(..)
|
| ty::PredicateKind::Coerce(..)
|
||||||
|
|
|
@ -44,9 +44,10 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
|
|
||||||
// Sometimes when we ask what it takes for T: WF, we get back that
|
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||||
// U: WF is required; in that case, we push U onto this stack and
|
// U: WF is required; in that case, we push U onto this stack and
|
||||||
// process it next. Currently (at least) these resulting
|
// process it next. Because the resulting predicates aren't always
|
||||||
// predicates are always guaranteed to be a subset of the original
|
// guaranteed to be a subset of the original type, so we need to store the
|
||||||
// type, so we need not fear non-termination.
|
// WF args we've computed in a set.
|
||||||
|
let mut checked_wf_args = rustc_data_structures::stable_set::FxHashSet::default();
|
||||||
let mut wf_args = vec![ty.into()];
|
let mut wf_args = vec![ty.into()];
|
||||||
|
|
||||||
let mut implied_bounds = vec![];
|
let mut implied_bounds = vec![];
|
||||||
|
@ -54,6 +55,10 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
let mut fulfill_cx = FulfillmentContext::new();
|
let mut fulfill_cx = FulfillmentContext::new();
|
||||||
|
|
||||||
while let Some(arg) = wf_args.pop() {
|
while let Some(arg) = wf_args.pop() {
|
||||||
|
if !checked_wf_args.insert(arg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the obligations for `arg` to be well-formed. If `arg` is
|
// Compute the obligations for `arg` to be well-formed. If `arg` is
|
||||||
// an unresolved inference variable, just substituted an empty set
|
// an unresolved inference variable, just substituted an empty set
|
||||||
// -- because the return type here is going to be things we *add*
|
// -- because the return type here is going to be things we *add*
|
||||||
|
|
|
@ -206,6 +206,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
|
||||||
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
|
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
|
||||||
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
|
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
|
||||||
}
|
}
|
||||||
|
(ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => {
|
||||||
|
relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok()
|
||||||
|
}
|
||||||
_ => predicate == p,
|
_ => predicate == p,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1807,6 +1807,12 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirI
|
||||||
let implied_obligations = traits::elaborate_predicates_with_span(fcx.tcx, predicates_with_span);
|
let implied_obligations = traits::elaborate_predicates_with_span(fcx.tcx, predicates_with_span);
|
||||||
|
|
||||||
for obligation in implied_obligations {
|
for obligation in implied_obligations {
|
||||||
|
// We lower empty bounds like `Vec<dyn Copy>:` as
|
||||||
|
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
|
||||||
|
// regular WF checking
|
||||||
|
if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let pred = obligation.predicate;
|
let pred = obligation.predicate;
|
||||||
// Match the existing behavior.
|
// Match the existing behavior.
|
||||||
if pred.is_global() && !pred.has_late_bound_regions() {
|
if pred.is_global() && !pred.has_late_bound_regions() {
|
||||||
|
|
|
@ -2264,12 +2264,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
||||||
// compiler/tooling bugs from not handling WF predicates.
|
// compiler/tooling bugs from not handling WF predicates.
|
||||||
} else {
|
} else {
|
||||||
let span = bound_pred.bounded_ty.span;
|
let span = bound_pred.bounded_ty.span;
|
||||||
let re_root_empty = tcx.lifetimes.re_root_empty;
|
|
||||||
let predicate = ty::Binder::bind_with_vars(
|
let predicate = ty::Binder::bind_with_vars(
|
||||||
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
|
ty::PredicateKind::WellFormed(ty.into()),
|
||||||
ty,
|
|
||||||
re_root_empty,
|
|
||||||
)),
|
|
||||||
bound_vars,
|
bound_vars,
|
||||||
);
|
);
|
||||||
predicates.insert((predicate.to_predicate(tcx), span));
|
predicates.insert((predicate.to_predicate(tcx), span));
|
||||||
|
|
|
@ -288,10 +288,10 @@ impl<'tcx> Clean<'tcx, Option<WherePredicate>> for ty::Predicate<'tcx> {
|
||||||
ty::PredicateKind::TypeOutlives(pred) => pred.clean(cx),
|
ty::PredicateKind::TypeOutlives(pred) => pred.clean(cx),
|
||||||
ty::PredicateKind::Projection(pred) => Some(pred.clean(cx)),
|
ty::PredicateKind::Projection(pred) => Some(pred.clean(cx)),
|
||||||
ty::PredicateKind::ConstEvaluatable(..) => None,
|
ty::PredicateKind::ConstEvaluatable(..) => None,
|
||||||
|
ty::PredicateKind::WellFormed(..) => None,
|
||||||
|
|
||||||
ty::PredicateKind::Subtype(..)
|
ty::PredicateKind::Subtype(..)
|
||||||
| ty::PredicateKind::Coerce(..)
|
| ty::PredicateKind::Coerce(..)
|
||||||
| ty::PredicateKind::WellFormed(..)
|
|
||||||
| ty::PredicateKind::ObjectSafe(..)
|
| ty::PredicateKind::ObjectSafe(..)
|
||||||
| ty::PredicateKind::ClosureKind(..)
|
| ty::PredicateKind::ClosureKind(..)
|
||||||
| ty::PredicateKind::ConstEquate(..)
|
| ty::PredicateKind::ConstEquate(..)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue