Skip to content

Commit 5182cc1

Browse files
committed
Auto merge of #55318 - Aaron1011:fix/final-auto-trait-resolve, r=nikomatsakis
Ensure that Rustdoc 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.
2 parents 118e052 + 9139374 commit 5182cc1

File tree

3 files changed

+152
-17
lines changed

3 files changed

+152
-17
lines changed

src/librustc/traits/auto_trait.rs

+81-17
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)) => {
@@ -369,7 +374,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
369374
}
370375
&Ok(None) => {}
371376
&Err(SelectionError::Unimplemented) => {
372-
if self.is_of_param(pred.skip_binder().trait_ref.substs) {
377+
if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) {
373378
already_visited.remove(&pred);
374379
self.add_user_pred(
375380
&mut user_computed_preds,
@@ -631,18 +636,28 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
631636
finished_map
632637
}
633638

634-
pub fn is_of_param(&self, substs: &Substs<'_>) -> bool {
635-
if substs.is_noop() {
636-
return false;
637-
}
639+
fn is_param_no_infer(&self, substs: &Substs<'_>) -> bool {
640+
return self.is_of_param(substs.type_at(0)) &&
641+
!substs.types().any(|t| t.has_infer_types());
642+
}
638643

639-
return match substs.type_at(0).sty {
644+
pub fn is_of_param(&self, ty: Ty<'_>) -> bool {
645+
return match ty.sty {
640646
ty::Param(_) => true,
641-
ty::Projection(p) => self.is_of_param(p.substs),
647+
ty::Projection(p) => self.is_of_param(p.self_ty()),
642648
_ => false,
643649
};
644650
}
645651

652+
fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool {
653+
match p.ty().skip_binder().sty {
654+
ty::Projection(proj) if proj == p.skip_binder().projection_ty => {
655+
true
656+
},
657+
_ => false
658+
}
659+
}
660+
646661
pub fn evaluate_nested_obligations<
647662
'b,
648663
'c,
@@ -661,28 +676,77 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
661676
) -> bool {
662677
let dummy_cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID);
663678

664-
for (obligation, predicate) in nested
665-
.filter(|o| o.recursion_depth == 1)
679+
for (obligation, mut predicate) in nested
666680
.map(|o| (o.clone(), o.predicate.clone()))
667681
{
668682
let is_new_pred =
669683
fresh_preds.insert(self.clean_pred(select.infcx(), predicate.clone()));
670684

685+
// Resolve any inference variables that we can, to help selection succeed
686+
predicate = select.infcx().resolve_type_vars_if_possible(&predicate);
687+
688+
// We only add a predicate as a user-displayable bound if
689+
// it involves a generic parameter, and doesn't contain
690+
// any inference variables.
691+
//
692+
// Displaying a bound involving a concrete type (instead of a generic
693+
// parameter) would be pointless, since it's always true
694+
// (e.g. u8: Copy)
695+
// Displaying an inference variable is impossible, since they're
696+
// an internal compiler detail without a defined visual representation
697+
//
698+
// We check this by calling is_of_param on the relevant types
699+
// from the various possible predicates
671700
match &predicate {
672701
&ty::Predicate::Trait(ref p) => {
673-
let substs = &p.skip_binder().trait_ref.substs;
702+
if self.is_param_no_infer(p.skip_binder().trait_ref.substs)
703+
&& !only_projections
704+
&& is_new_pred {
674705

675-
if self.is_of_param(substs) && !only_projections && is_new_pred {
676706
self.add_user_pred(computed_preds, predicate);
677707
}
678708
predicates.push_back(p.clone());
679709
}
680710
&ty::Predicate::Projection(p) => {
681-
// If the projection isn't all type vars, then
682-
// we don't want to add it as a bound
683-
if self.is_of_param(p.skip_binder().projection_ty.substs) && is_new_pred {
684-
self.add_user_pred(computed_preds, predicate);
685-
} else {
711+
debug!("evaluate_nested_obligations: examining projection predicate {:?}",
712+
predicate);
713+
714+
// As described above, we only want to display
715+
// bounds which include a generic parameter but don't include
716+
// an inference variable.
717+
// Additionally, we check if we've seen this predicate before,
718+
// to avoid rendering duplicate bounds to the user.
719+
if self.is_param_no_infer(p.skip_binder().projection_ty.substs)
720+
&& !p.ty().skip_binder().is_ty_infer()
721+
&& is_new_pred {
722+
debug!("evaluate_nested_obligations: adding projection predicate\
723+
to computed_preds: {:?}", predicate);
724+
725+
// Under unusual circumstances, we can end up with a self-refeential
726+
// projection predicate. For example:
727+
// <T as MyType>::Value == <T as MyType>::Value
728+
// Not only is displaying this to the user pointless,
729+
// having it in the ParamEnv will cause an issue if we try to call
730+
// poly_project_and_unify_type on the predicate, since this kind of
731+
// predicate will normally never end up in a ParamEnv.
732+
//
733+
// For these reasons, we ignore these weird predicates,
734+
// ensuring that we're able to properly synthesize an auto trait impl
735+
if self.is_self_referential_projection(p) {
736+
debug!("evaluate_nested_obligations: encountered a projection
737+
predicate equating a type with itself! Skipping");
738+
739+
} else {
740+
self.add_user_pred(computed_preds, predicate);
741+
}
742+
}
743+
744+
// We can only call poly_project_and_unify_type when our predicate's
745+
// Ty is an inference variable - otherwise, there won't be anything to
746+
// unify
747+
if p.ty().skip_binder().is_ty_infer() {
748+
debug!("Projecting and unifying projection predicate {:?}",
749+
predicate);
686750
match poly_project_and_unify_type(select, &obligation.with(p.clone())) {
687751
Err(e) => {
688752
debug!(

src/test/rustdoc/issue-50159.rs

+31
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
// Some unusual code minimized from
12+
// https://github.com/sile/handy_async/tree/7b619b762c06544fc67792c8ff8ebc24a88fdb98
13+
14+
pub trait Pattern {
15+
type Value;
16+
}
17+
18+
pub struct Constrain<A, B = A, C = A>(A, B, C);
19+
20+
impl<A, B, C> Pattern for Constrain<A, B, C>
21+
where A: Pattern,
22+
B: Pattern<Value = A::Value>,
23+
C: Pattern<Value = A::Value>,
24+
{
25+
type Value = A::Value;
26+
}
27+
28+
pub struct Wrapper<T>(T);
29+
30+
impl<T> Pattern for Wrapper<T> {
31+
type Value = T;
32+
}
33+
34+
35+
// @has self_referential/struct.WriteAndThen.html
36+
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//*/code' "impl<P1> Send for \
37+
// WriteAndThen<P1> where <P1 as Pattern>::Value: Send"
38+
pub struct WriteAndThen<P1>(pub P1::Value,pub <Constrain<P1, Wrapper<P1::Value>> as Pattern>::Value)
39+
where P1: Pattern;
40+

0 commit comments

Comments
 (0)