Skip to content

Commit 1939722

Browse files
committed
Ensure that Rusdoc discovers all necessary auto trait bounds
Fixes #50159 This commit makes several improvements to AutoTraitFinder: * Call infcx.resolve_type_vars_if_possible before processing new predicates. This ensures that we eliminate inference variables wherever possible. * Process all nested obligations we get from a vtable, not just ones with depth=1. * The 'depth=1' check was a hack to work around issues processing certain predicates. The other changes in this commit allow us to properly process all predicates that we encounter, so the check is no longer necessary, * Ensure that we only display predicates *without* inference variables to the user, and only attempt to unify predicates that *have* an inference variable as their type. Additionally, the internal helper method is_of_param now operates directly on a type, rather than taking a Substs. This allows us to use the 'self_ty' method, rather than directly dealing with Substs.
1 parent f99911a commit 1939722

File tree

2 files changed

+82
-17
lines changed

2 files changed

+82
-17
lines changed

src/librustc/traits/auto_trait.rs

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,12 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
334334
continue;
335335
}
336336

337-
let result = select.select(&Obligation::new(dummy_cause.clone(), new_env, pred));
337+
// Call infcx.resolve_type_vars_if_possible to see if we can
338+
// get rid of any inference variables.
339+
let obligation = infcx.resolve_type_vars_if_possible(
340+
&Obligation::new(dummy_cause.clone(), new_env, pred)
341+
);
342+
let result = select.select(&obligation);
338343

339344
match &result {
340345
&Ok(Some(ref vtable)) => {
@@ -354,7 +359,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
354359
}
355360
&Ok(None) => {}
356361
&Err(SelectionError::Unimplemented) => {
357-
if self.is_of_param(pred.skip_binder().trait_ref.substs) {
362+
if self.is_of_param(pred.skip_binder().self_ty()) {
358363
already_visited.remove(&pred);
359364
self.add_user_pred(
360365
&mut user_computed_preds,
@@ -592,14 +597,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
592597
finished_map
593598
}
594599

595-
pub fn is_of_param(&self, substs: &Substs<'_>) -> bool {
596-
if substs.is_noop() {
597-
return false;
598-
}
599-
600-
return match substs.type_at(0).sty {
600+
pub fn is_of_param(&self, ty: Ty<'_>) -> bool {
601+
return match ty.sty {
601602
ty::Param(_) => true,
602-
ty::Projection(p) => self.is_of_param(p.substs),
603+
ty::Projection(p) => self.is_of_param(p.self_ty()),
603604
_ => false,
604605
};
605606
}
@@ -622,28 +623,61 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
622623
) -> bool {
623624
let dummy_cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID);
624625

625-
for (obligation, predicate) in nested
626-
.filter(|o| o.recursion_depth == 1)
626+
for (obligation, mut predicate) in nested
627627
.map(|o| (o.clone(), o.predicate.clone()))
628628
{
629629
let is_new_pred =
630630
fresh_preds.insert(self.clean_pred(select.infcx(), predicate.clone()));
631631

632+
// Resolve any inference variables that we can, to help selection succeed
633+
predicate = select.infcx().resolve_type_vars_if_possible(&predicate);
634+
635+
// We only add a predicate as a user-displayable bound if
636+
// it involves a generic parameter, and doesn't contain
637+
// any inference variables.
638+
//
639+
// Displaying a bound involving a concrete type (instead of a generic
640+
// parameter) would be pointless, since it's always true
641+
// (e.g. u8: Copy)
642+
// Displaying an inference variable is impossible, since they're
643+
// an internal compiler detail without a defined visual representation
644+
//
645+
// We check this by calling is_of_param on the relevant types
646+
// from the various possible predicates
632647
match &predicate {
633648
&ty::Predicate::Trait(ref p) => {
634-
let substs = &p.skip_binder().trait_ref.substs;
649+
if self.is_of_param(p.skip_binder().self_ty())
650+
&& !only_projections
651+
&& is_new_pred {
635652

636-
if self.is_of_param(substs) && !only_projections && is_new_pred {
637653
self.add_user_pred(computed_preds, predicate);
638654
}
639655
predicates.push_back(p.clone());
640656
}
641657
&ty::Predicate::Projection(p) => {
642-
// If the projection isn't all type vars, then
643-
// we don't want to add it as a bound
644-
if self.is_of_param(p.skip_binder().projection_ty.substs) && is_new_pred {
658+
debug!("evaluate_nested_obligations: examining projection predicate {:?}",
659+
predicate);
660+
661+
// As described above, we only want to display
662+
// bounds which include a generic parameter but don't include
663+
// an inference variable.
664+
// Additionally, we check if we've seen this predicate before,
665+
// to avoid rendering duplicate bounds to the user.
666+
if self.is_of_param(p.skip_binder().projection_ty.self_ty())
667+
&& !p.ty().skip_binder().is_ty_infer()
668+
&& is_new_pred {
669+
debug!("evaluate_nested_obligations: adding projection predicate\
670+
to computed_preds: {:?}", predicate);
671+
645672
self.add_user_pred(computed_preds, predicate);
646-
} else {
673+
}
674+
675+
// We can only call poly_project_and_unify_type when our predicate's
676+
// Ty is an inference variable - otherwise, there won't be anything to
677+
// unify
678+
if p.ty().skip_binder().is_ty_infer() {
679+
debug!("Projecting and unifying projection predicate {:?}",
680+
predicate);
647681
match poly_project_and_unify_type(select, &obligation.with(p.clone())) {
648682
Err(e) => {
649683
debug!(

src/test/rustdoc/issue-50159.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
pub trait Signal {
13+
type Item;
14+
}
15+
16+
pub trait Signal2 {
17+
type Item2;
18+
}
19+
20+
impl<B, C> Signal2 for B where B: Signal<Item = C> {
21+
type Item2 = C;
22+
}
23+
24+
// @has issue_50159/struct.Switch.html
25+
// @has - '//code' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send'
26+
// @has - '//code' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync'
27+
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
28+
// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 2
29+
pub struct Switch<B: Signal> {
30+
pub inner: <B as Signal2>::Item2,
31+
}

0 commit comments

Comments
 (0)