Skip to content

Commit 116edb6

Browse files
committed
Auto merge of #98542 - jackh726:coinductive-wf, r=oli-obk
Make empty bounds lower to `WellFormed` and make `WellFormed` coinductive r? rust-lang/types
2 parents 126e3df + bd298ad commit 116edb6

File tree

8 files changed

+130
-26
lines changed

8 files changed

+130
-26
lines changed

compiler/rustc_privacy/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ where
154154
}
155155
ControlFlow::CONTINUE
156156
}
157+
ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
157158
_ => bug!("unexpected predicate: {:?}", predicate),
158159
}
159160
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+100-15
Original file line numberDiff line numberDiff line change
@@ -488,20 +488,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
488488
}
489489
}
490490

491-
ty::PredicateKind::WellFormed(arg) => match wf::obligations(
492-
self.infcx,
493-
obligation.param_env,
494-
obligation.cause.body_id,
495-
obligation.recursion_depth + 1,
496-
arg,
497-
obligation.cause.span,
498-
) {
499-
Some(mut obligations) => {
500-
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
501-
self.evaluate_predicates_recursively(previous_stack, obligations)
491+
ty::PredicateKind::WellFormed(arg) => {
492+
// So, there is a bit going on here. First, `WellFormed` predicates
493+
// are coinductive, like trait predicates with auto traits.
494+
// This means that we need to detect if we have recursively
495+
// evaluated `WellFormed(X)`. Otherwise, we would run into
496+
// a "natural" overflow error.
497+
//
498+
// Now, the next question is whether we need to do anything
499+
// special with caching. Considering the following tree:
500+
// - `WF(Foo<T>)`
501+
// - `Bar<T>: Send`
502+
// - `WF(Foo<T>)`
503+
// - `Foo<T>: Trait`
504+
// In this case, the innermost `WF(Foo<T>)` should return
505+
// `EvaluatedToOk`, since it's coinductive. Then if
506+
// `Bar<T>: Send` is resolved to `EvaluatedToOk`, it can be
507+
// inserted into a cache (because without thinking about `WF`
508+
// goals, it isn't in a cycle). If `Foo<T>: Trait` later doesn't
509+
// hold, then `Bar<T>: Send` shouldn't hold. Therefore, we
510+
// *do* need to keep track of coinductive cycles.
511+
512+
let cache = previous_stack.cache;
513+
let dfn = cache.next_dfn();
514+
515+
for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() {
516+
if stack_arg.0 != arg {
517+
continue;
518+
}
519+
debug!("WellFormed({:?}) on stack", arg);
520+
if let Some(stack) = previous_stack.head {
521+
// Okay, let's imagine we have two different stacks:
522+
// `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait`
523+
// `WF(T) -> T: NonAutoTrait -> WF(T)`
524+
// Because of this, we need to check that all
525+
// predicates between the WF goals are coinductive.
526+
// Otherwise, we can say that `T: NonAutoTrait` is
527+
// true.
528+
// Let's imagine we have a predicate stack like
529+
// `Foo: Bar -> WF(T) -> T: NonAutoTrait -> T: Auto
530+
// depth ^1 ^2 ^3
531+
// and the current predicate is `WF(T)`. `wf_args`
532+
// would contain `(T, 1)`. We want to check all
533+
// trait predicates greater than `1`. The previous
534+
// stack would be `T: Auto`.
535+
let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1);
536+
let tcx = self.tcx();
537+
let cycle =
538+
cycle.map(|stack| stack.obligation.predicate.to_predicate(tcx));
539+
if self.coinductive_match(cycle) {
540+
stack.update_reached_depth(stack_arg.1);
541+
return Ok(EvaluatedToOk);
542+
} else {
543+
return Ok(EvaluatedToRecur);
544+
}
545+
}
546+
return Ok(EvaluatedToOk);
502547
}
503-
None => Ok(EvaluatedToAmbig),
504-
},
548+
549+
match wf::obligations(
550+
self.infcx,
551+
obligation.param_env,
552+
obligation.cause.body_id,
553+
obligation.recursion_depth + 1,
554+
arg,
555+
obligation.cause.span,
556+
) {
557+
Some(mut obligations) => {
558+
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
559+
560+
cache.wf_args.borrow_mut().push((arg, previous_stack.depth()));
561+
let result =
562+
self.evaluate_predicates_recursively(previous_stack, obligations);
563+
cache.wf_args.borrow_mut().pop();
564+
565+
let result = result?;
566+
567+
if !result.must_apply_modulo_regions() {
568+
cache.on_failure(dfn);
569+
}
570+
571+
cache.on_completion(dfn);
572+
573+
Ok(result)
574+
}
575+
None => Ok(EvaluatedToAmbig),
576+
}
577+
}
505578

506579
ty::PredicateKind::TypeOutlives(pred) => {
507580
// A global type with no late-bound regions can only
@@ -718,6 +791,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
718791

719792
debug!(?fresh_trait_pred);
720793

794+
// If a trait predicate is in the (local or global) evaluation cache,
795+
// then we know it holds without cycles.
721796
if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {
722797
debug!(?result, "CACHE HIT");
723798
return Ok(result);
@@ -921,7 +996,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
921996
/// - it also appears in the backtrace at some position `X`,
922997
/// - all the predicates at positions `X..` between `X` and the top are
923998
/// also defaulted traits.
924-
pub fn coinductive_match<I>(&mut self, mut cycle: I) -> bool
999+
pub(crate) fn coinductive_match<I>(&mut self, mut cycle: I) -> bool
9251000
where
9261001
I: Iterator<Item = ty::Predicate<'tcx>>,
9271002
{
@@ -931,6 +1006,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
9311006
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
9321007
let result = match predicate.kind().skip_binder() {
9331008
ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()),
1009+
ty::PredicateKind::WellFormed(_) => true,
9341010
_ => false,
9351011
};
9361012
debug!(?predicate, ?result, "coinductive_predicate");
@@ -2410,6 +2486,15 @@ struct ProvisionalEvaluationCache<'tcx> {
24102486
/// all cache values whose DFN is >= 4 -- in this case, that
24112487
/// means the cached value for `F`.
24122488
map: RefCell<FxHashMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>,
2489+
2490+
/// The stack of args that we assume to be true because a `WF(arg)` predicate
2491+
/// is on the stack above (and because of wellformedness is coinductive).
2492+
/// In an "ideal" world, this would share a stack with trait predicates in
2493+
/// `TraitObligationStack`. However, trait predicates are *much* hotter than
2494+
/// `WellFormed` predicates, and it's very likely that the additional matches
2495+
/// will have a perf effect. The value here is the well-formed `GenericArg`
2496+
/// and the depth of the trait predicate *above* that well-formed predicate.
2497+
wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>,
24132498
}
24142499

24152500
/// A cache value for the provisional cache: contains the depth-first
@@ -2423,7 +2508,7 @@ struct ProvisionalEvaluation {
24232508

24242509
impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
24252510
fn default() -> Self {
2426-
Self { dfn: Cell::new(0), map: Default::default() }
2511+
Self { dfn: Cell::new(0), map: Default::default(), wf_args: Default::default() }
24272512
}
24282513
}
24292514

compiler/rustc_traits/src/chalk/lowering.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,16 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
106106
ty::PredicateKind::Projection(predicate) => chalk_ir::DomainGoal::Holds(
107107
chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
108108
),
109-
ty::PredicateKind::WellFormed(..)
110-
| ty::PredicateKind::ObjectSafe(..)
109+
ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
110+
ty::GenericArgKind::Type(ty) => chalk_ir::DomainGoal::WellFormed(
111+
chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
112+
),
113+
// FIXME(chalk): we need to change `WellFormed` in Chalk to take a `GenericArg`
114+
_ => chalk_ir::DomainGoal::WellFormed(chalk_ir::WellFormed::Ty(
115+
interner.tcx.types.unit.lower_into(interner),
116+
)),
117+
},
118+
ty::PredicateKind::ObjectSafe(..)
111119
| ty::PredicateKind::ClosureKind(..)
112120
| ty::PredicateKind::Subtype(..)
113121
| ty::PredicateKind::Coerce(..)

compiler/rustc_traits/src/implied_outlives_bounds.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,21 @@ fn compute_implied_outlives_bounds<'tcx>(
4444

4545
// Sometimes when we ask what it takes for T: WF, we get back that
4646
// U: WF is required; in that case, we push U onto this stack and
47-
// process it next. Currently (at least) these resulting
48-
// predicates are always guaranteed to be a subset of the original
49-
// type, so we need not fear non-termination.
47+
// process it next. Because the resulting predicates aren't always
48+
// guaranteed to be a subset of the original type, so we need to store the
49+
// WF args we've computed in a set.
50+
let mut checked_wf_args = rustc_data_structures::stable_set::FxHashSet::default();
5051
let mut wf_args = vec![ty.into()];
5152

5253
let mut implied_bounds = vec![];
5354

5455
let mut fulfill_cx = FulfillmentContext::new();
5556

5657
while let Some(arg) = wf_args.pop() {
58+
if !checked_wf_args.insert(arg) {
59+
continue;
60+
}
61+
5762
// Compute the obligations for `arg` to be well-formed. If `arg` is
5863
// an unresolved inference variable, just substituted an empty set
5964
// -- because the return type here is going to be things we *add*

compiler/rustc_typeck/src/check/dropck.rs

+3
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
206206
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
207207
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
208208
}
209+
(ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => {
210+
relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok()
211+
}
209212
_ => predicate == p,
210213
}
211214
};

compiler/rustc_typeck/src/check/wfcheck.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,12 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirI
18071807
let implied_obligations = traits::elaborate_predicates_with_span(fcx.tcx, predicates_with_span);
18081808

18091809
for obligation in implied_obligations {
1810+
// We lower empty bounds like `Vec<dyn Copy>:` as
1811+
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
1812+
// regular WF checking
1813+
if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() {
1814+
continue;
1815+
}
18101816
let pred = obligation.predicate;
18111817
// Match the existing behavior.
18121818
if pred.is_global() && !pred.has_late_bound_regions() {

compiler/rustc_typeck/src/collect.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -2264,12 +2264,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
22642264
// compiler/tooling bugs from not handling WF predicates.
22652265
} else {
22662266
let span = bound_pred.bounded_ty.span;
2267-
let re_root_empty = tcx.lifetimes.re_root_empty;
22682267
let predicate = ty::Binder::bind_with_vars(
2269-
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
2270-
ty,
2271-
re_root_empty,
2272-
)),
2268+
ty::PredicateKind::WellFormed(ty.into()),
22732269
bound_vars,
22742270
);
22752271
predicates.insert((predicate.to_predicate(tcx), span));

src/librustdoc/clean/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,10 @@ impl<'tcx> Clean<'tcx, Option<WherePredicate>> for ty::Predicate<'tcx> {
293293
ty::PredicateKind::TypeOutlives(pred) => pred.clean(cx),
294294
ty::PredicateKind::Projection(pred) => Some(pred.clean(cx)),
295295
ty::PredicateKind::ConstEvaluatable(..) => None,
296+
ty::PredicateKind::WellFormed(..) => None,
296297

297298
ty::PredicateKind::Subtype(..)
298299
| ty::PredicateKind::Coerce(..)
299-
| ty::PredicateKind::WellFormed(..)
300300
| ty::PredicateKind::ObjectSafe(..)
301301
| ty::PredicateKind::ClosureKind(..)
302302
| ty::PredicateKind::ConstEquate(..)

0 commit comments

Comments
 (0)