Skip to content

Commit 4b8089d

Browse files
committed
Auto merge of #52990 - Aaron1011:fix/rustdoc-auto-trait-static, r=eddyb
Fix ICE when rustdoc encounters certain usages of HRTBs Fixes #51236 Under certain circumstances, `AutoTraitFinder` could end up computing a `ParamEnv` involving two trait predicates that differed only in the region parameters involved. One of these parameters would be a HRTB, while the other would be a normal region parameter. When this `ParamEnv` was later passed to `SelectionContext`, an `Ambiguity` error would occur, since the erased versions of these predicates would be identical. To solve the issue, we de-duplicate our list of predicates as we build it up. Whenever we encounter two predicates that differ only in their assignment of region parameters (a HRTB vs a normal lifetime parameter), we pick the HRTB. This corresponds to selecting a 'stricter' bound to display in the generated documentation: we're requiring that a particular type works for all possible lifetime parameters if it's going to implement a particular auto trait.
2 parents 78ec12d + b010d1f commit 4b8089d

File tree

4 files changed

+136
-5
lines changed

4 files changed

+136
-5
lines changed

src/librustc/traits/auto_trait.rs

+90-3
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
358358
&Err(SelectionError::Unimplemented) => {
359359
if self.is_of_param(pred.skip_binder().trait_ref.substs) {
360360
already_visited.remove(&pred);
361-
user_computed_preds.insert(ty::Predicate::Trait(pred.clone()));
361+
self.add_user_pred(&mut user_computed_preds,
362+
ty::Predicate::Trait(pred.clone()));
362363
predicates.push_back(pred);
363364
} else {
364365
debug!(
@@ -393,6 +394,92 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
393394
return Some((new_env, final_user_env));
394395
}
395396

397+
// This method is designed to work around the following issue:
398+
// When we compute auto trait bounds, we repeatedly call SelectionContext.select,
399+
// progressively building a ParamEnv based on the results we get.
400+
// However, our usage of SelectionContext differs from its normal use within the compiler,
401+
// in that we capture and re-reprocess predicates from Unimplemented errors.
402+
//
403+
// This can lead to a corner case when dealing with region parameters.
404+
// During our selection loop in evaluate_predicates, we might end up with
405+
// two trait predicates that differ only in their region parameters:
406+
// one containing a HRTB lifetime parameter, and one containing a 'normal'
407+
// lifetime parameter. For example:
408+
//
409+
// T as MyTrait<'a>
410+
// T as MyTrait<'static>
411+
//
412+
// If we put both of these predicates in our computed ParamEnv, we'll
413+
// confuse SelectionContext, since it will (correctly) view both as being applicable.
414+
//
415+
// To solve this, we pick the 'more strict' lifetime bound - i.e. the HRTB
416+
// Our end goal is to generate a user-visible description of the conditions
417+
// under which a type implements an auto trait. A trait predicate involving
418+
// a HRTB means that the type needs to work with any choice of lifetime,
419+
// not just one specific lifetime (e.g. 'static).
420+
fn add_user_pred<'c>(&self, user_computed_preds: &mut FxHashSet<ty::Predicate<'c>>,
421+
new_pred: ty::Predicate<'c>) {
422+
let mut should_add_new = true;
423+
user_computed_preds.retain(|&old_pred| {
424+
match (&new_pred, old_pred) {
425+
(&ty::Predicate::Trait(new_trait), ty::Predicate::Trait(old_trait)) => {
426+
if new_trait.def_id() == old_trait.def_id() {
427+
let new_substs = new_trait.skip_binder().trait_ref.substs;
428+
let old_substs = old_trait.skip_binder().trait_ref.substs;
429+
if !new_substs.types().eq(old_substs.types()) {
430+
// We can't compare lifetimes if the types are different,
431+
// so skip checking old_pred
432+
return true
433+
}
434+
435+
for (new_region, old_region) in new_substs
436+
.regions()
437+
.zip(old_substs.regions()) {
438+
439+
match (new_region, old_region) {
440+
// If both predicates have an 'ReLateBound' (a HRTB) in the
441+
// same spot, we do nothing
442+
(
443+
ty::RegionKind::ReLateBound(_, _),
444+
ty::RegionKind::ReLateBound(_, _)
445+
) => {},
446+
447+
(ty::RegionKind::ReLateBound(_, _), _) => {
448+
// The new predicate has a HRTB in a spot where the old
449+
// predicate does not (if they both had a HRTB, the previous
450+
// match arm would have executed).
451+
//
452+
// The means we want to remove the older predicate from
453+
// user_computed_preds, since having both it and the new
454+
// predicate in a ParamEnv would confuse SelectionContext
455+
// We're currently in the predicate passed to 'retain',
456+
// so we return 'false' to remove the old predicate from
457+
// user_computed_preds
458+
return false;
459+
},
460+
(_, ty::RegionKind::ReLateBound(_, _)) => {
461+
// This is the opposite situation as the previous arm - the
462+
// old predicate has a HRTB lifetime in a place where the
463+
// new predicate does not. We want to leave the old
464+
// predicate in user_computed_preds, and skip adding
465+
// new_pred to user_computed_params.
466+
should_add_new = false
467+
}
468+
_ => {}
469+
}
470+
}
471+
}
472+
},
473+
_ => {}
474+
}
475+
return true
476+
});
477+
478+
if should_add_new {
479+
user_computed_preds.insert(new_pred);
480+
}
481+
}
482+
396483
pub fn region_name(&self, region: Region) -> Option<String> {
397484
match region {
398485
&ty::ReEarlyBound(r) => Some(r.name.to_string()),
@@ -555,15 +642,15 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
555642
let substs = &p.skip_binder().trait_ref.substs;
556643

557644
if self.is_of_param(substs) && !only_projections && is_new_pred {
558-
computed_preds.insert(predicate);
645+
self.add_user_pred(computed_preds, predicate);
559646
}
560647
predicates.push_back(p.clone());
561648
}
562649
&ty::Predicate::Projection(p) => {
563650
// If the projection isn't all type vars, then
564651
// we don't want to add it as a bound
565652
if self.is_of_param(p.skip_binder().projection_ty.substs) && is_new_pred {
566-
computed_preds.insert(predicate);
653+
self.add_user_pred(computed_preds, predicate);
567654
} else {
568655
match poly_project_and_unify_type(select, &obligation.with(p.clone())) {
569656
Err(e) => {

src/librustdoc/clean/auto_trait.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ impl<'a, 'tcx, 'rcx, 'cstore> AutoTraitFinder<'a, 'tcx, 'rcx, 'cstore> {
418418
name: name.to_string(),
419419
kind: GenericParamDefKind::Lifetime,
420420
})
421-
}
422-
&ty::ReVar(_) | &ty::ReEarlyBound(_) => None,
421+
},
422+
&ty::ReVar(_) | &ty::ReEarlyBound(_) | &ty::ReStatic => None,
423423
_ => panic!("Unexpected region type {:?}", r),
424424
}
425425
})

src/test/rustdoc/issue-51236.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
use std::marker::PhantomData;
12+
13+
pub mod traits {
14+
pub trait Owned<'a> {
15+
type Reader;
16+
}
17+
}
18+
19+
// @has issue_51236/struct.Owned.html
20+
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//*/code' "impl<T> Send for \
21+
// Owned<T> where <T as Owned<'static>>::Reader: Send"
22+
pub struct Owned<T> where T: for<'a> ::traits::Owned<'a> {
23+
marker: PhantomData<<T as ::traits::Owned<'static>>::Reader>,
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
pub trait OwnedTrait<'a> {
12+
type Reader;
13+
}
14+
15+
// @has static_region/struct.Owned.html
16+
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//*/code' "impl<T> Send for \
17+
// Owned<T> where <T as OwnedTrait<'static>>::Reader: Send"
18+
pub struct Owned<T> where T: OwnedTrait<'static> {
19+
marker: <T as OwnedTrait<'static>>::Reader,
20+
}

0 commit comments

Comments
 (0)