Skip to content

Commit 4501d3a

Browse files
committed
Autotrait bounds on dyn-safe trait methods
1 parent 9e1c600 commit 4501d3a

File tree

4 files changed

+110
-10
lines changed

4 files changed

+110
-10
lines changed

compiler/rustc_hir_analysis/src/coherence/orphan.rs

+22
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ fn do_orphan_check_impl<'tcx>(
115115
// impl MyAuto for dyn Trait {} // NOT OKAY
116116
// impl<T: ?Sized> MyAuto for T {} // NOT OKAY
117117
//
118+
// With this restriction, it's guaranteed that an auto-trait is
119+
// implemented for a trait object if and only if the auto-trait is one
120+
// of the trait object's trait bounds (or a supertrait of a bound). In
121+
// other words `dyn Trait + AutoTrait` always implements AutoTrait,
122+
// while `dyn Trait` never implements AutoTrait.
123+
//
124+
// This is necessary in order for autotrait bounds on methods of trait
125+
// objects to be sound.
126+
//
127+
// auto trait AutoTrait {}
128+
//
129+
// trait ObjectSafeTrait {
130+
// fn f(&self) where Self: AutoTrait;
131+
// }
132+
//
133+
// We can allow f to be called on `dyn ObjectSafeTrait + AutoTrait`.
134+
//
135+
// If we didn't deny `impl AutoTrait for dyn Trait`, it would be unsound
136+
// for the ObjectSafeTrait shown above to be object safe because someone
137+
// could take some type implementing ObjectSafeTrait but not AutoTrait,
138+
// unsize it to `dyn ObjectSafeTrait`, and call .f() which has no
139+
// concrete implementation (issue #50781).
118140
enum LocalImpl {
119141
Allow,
120142
Disallow { problematic_kind: &'static str },

compiler/rustc_trait_selection/src/traits/object_safety.rs

+50-10
Original file line numberDiff line numberDiff line change
@@ -547,16 +547,56 @@ fn virtual_call_violation_for_method<'tcx>(
547547

548548
// NOTE: This check happens last, because it results in a lint, and not a
549549
// hard error.
550-
if tcx
551-
.predicates_of(method.def_id)
552-
.predicates
553-
.iter()
554-
// A trait object can't claim to live more than the concrete type,
555-
// so outlives predicates will always hold.
556-
.cloned()
557-
.filter(|(p, _)| p.to_opt_type_outlives().is_none())
558-
.any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred))
559-
{
550+
if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| {
551+
// dyn Trait is okay:
552+
//
553+
// trait Trait {
554+
// fn f(&self) where Self: 'static;
555+
// }
556+
//
557+
// because a trait object can't claim to live longer than the concrete
558+
// type. If the lifetime bound holds on dyn Trait then it's guaranteed
559+
// to hold as well on the concrete type.
560+
if pred.to_opt_type_outlives().is_some() {
561+
return false;
562+
}
563+
564+
// dyn Trait is okay:
565+
//
566+
// auto trait AutoTrait {}
567+
//
568+
// trait Trait {
569+
// fn f(&self) where Self: AutoTrait;
570+
// }
571+
//
572+
// because `impl AutoTrait for dyn Trait` is disallowed by coherence.
573+
// Traits with a default impl are implemented for a trait object if and
574+
// only if the autotrait is one of the trait object's trait bounds, like
575+
// in `dyn Trait + AutoTrait`. This guarantees that trait objects only
576+
// implement auto traits if the underlying type does as well.
577+
if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
578+
trait_ref: pred_trait_ref,
579+
constness: ty::BoundConstness::NotConst,
580+
polarity: ty::ImplPolarity::Positive,
581+
})) = pred.kind().skip_binder()
582+
&& pred_trait_ref.self_ty() == tcx.types.self_param
583+
&& tcx.trait_is_auto(pred_trait_ref.def_id)
584+
{
585+
// Consider bounds like `Self: Bound<Self>`. Auto traits are not
586+
// allowed to have generic parameters so `auto trait Bound<T> {}`
587+
// would already have reported an error at the definition of the
588+
// auto trait.
589+
if pred_trait_ref.substs.len() != 1 {
590+
tcx.sess.diagnostic().delay_span_bug(
591+
span,
592+
"auto traits cannot have generic parameters",
593+
);
594+
}
595+
return false;
596+
}
597+
598+
contains_illegal_self_type_reference(tcx, trait_def_id, pred.clone())
599+
}) {
560600
return Some(MethodViolationCode::WhereClauseReferencesSelf);
561601
}
562602

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// check-fail
2+
3+
#![feature(auto_traits)]
4+
#![deny(where_clauses_object_safety)]
5+
6+
auto trait AutoTrait {}
7+
8+
trait Trait {
9+
fn static_lifetime_bound(&self) where Self: 'static {}
10+
11+
fn arg_lifetime_bound<'a>(&self, _arg: &'a ()) where Self: 'a {}
12+
13+
fn autotrait_bound(&self) where Self: AutoTrait {}
14+
}
15+
16+
impl Trait for () {}
17+
18+
fn main() {
19+
let trait_object = &() as &dyn Trait;
20+
trait_object.static_lifetime_bound();
21+
trait_object.arg_lifetime_bound(&());
22+
trait_object.autotrait_bound(); //~ ERROR: the trait bound `dyn Trait: AutoTrait` is not satisfied
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied
2+
--> $DIR/self-in-where-clause-allowed.rs:22:18
3+
|
4+
LL | trait_object.autotrait_bound();
5+
| ^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait`
6+
|
7+
note: required by a bound in `Trait::autotrait_bound`
8+
--> $DIR/self-in-where-clause-allowed.rs:13:43
9+
|
10+
LL | fn autotrait_bound(&self) where Self: AutoTrait {}
11+
| ^^^^^^^^^ required by this bound in `Trait::autotrait_bound`
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)