Skip to content

Commit 18a88be

Browse files
committed
Let unsafe traits and autotraits be used as bound in dyn-safe trait
1 parent ef4046e commit 18a88be

File tree

6 files changed

+164
-10
lines changed

6 files changed

+164
-10
lines changed

compiler/rustc_trait_selection/src/traits/object_safety.rs

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -529,16 +529,72 @@ fn virtual_call_violation_for_method<'tcx>(
529529

530530
// NOTE: This check happens last, because it results in a lint, and not a
531531
// hard error.
532-
if tcx
533-
.predicates_of(method.def_id)
534-
.predicates
535-
.iter()
536-
// A trait object can't claim to live more than the concrete type,
537-
// so outlives predicates will always hold.
538-
.cloned()
539-
.filter(|(p, _)| p.to_opt_type_outlives().is_none())
540-
.any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred))
541-
{
532+
if tcx.predicates_of(method.def_id).predicates.iter().any(|(pred, _span)| {
533+
// dyn Trait is okay:
534+
//
535+
// trait Trait {
536+
// fn f(&self) where Self: 'static;
537+
// }
538+
//
539+
// because a trait object can't claim to live longer than the
540+
// concrete type. If the lifetime bound holds on dyn Trait then it's
541+
// guaranteed to hold as well on the concrete type.
542+
if pred.to_opt_type_outlives().is_some() {
543+
return false;
544+
}
545+
546+
if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
547+
trait_ref: pred_trait_ref,
548+
constness: ty::BoundConstness::NotConst,
549+
polarity: ty::ImplPolarity::Positive,
550+
})) = pred.kind().skip_binder()
551+
&& pred_trait_ref.self_ty() == tcx.types.self_param
552+
{
553+
let pred_trait_def = tcx.trait_def(pred_trait_ref.def_id);
554+
let mut disregard_self_in_self_ty = false;
555+
556+
// dyn Trait is okay:
557+
//
558+
// unsafe trait Bound {}
559+
//
560+
// trait Trait {
561+
// fn f(&self) where Self: Bound;
562+
// }
563+
//
564+
// because we don't need to worry about a potential `unsafe impl
565+
// Bound for dyn Trait`. Whoever wrote such an impl, it's their
566+
// fault when f gets called on a !Bound concrete type.
567+
if let hir::Unsafety::Unsafe = pred_trait_def.unsafety {
568+
disregard_self_in_self_ty = true;
569+
}
570+
571+
// dyn Trait is okay:
572+
//
573+
// extern crate other_crate {
574+
// auto trait Bound {}
575+
// }
576+
//
577+
// trait Trait {
578+
// fn f(&self) where Self: other_crate::Bound;
579+
// }
580+
//
581+
// because `impl Bound for dyn Trait` is impossible. Cross-crate
582+
// traits with a default impl can only be implemented for a
583+
// struct/enum type, not dyn Trait.
584+
if pred_trait_def.has_auto_impl && pred_trait_def.def_id.krate != trait_def_id.krate {
585+
disregard_self_in_self_ty = true;
586+
}
587+
588+
if disregard_self_in_self_ty {
589+
// Only check the rest of the bound's parameters. So `Self:
590+
// Bound<Self>` is still considered illegal.
591+
let rest_of_substs = &pred_trait_ref.substs[1..];
592+
return contains_illegal_self_type_reference(tcx, trait_def_id, rest_of_substs);
593+
}
594+
}
595+
596+
contains_illegal_self_type_reference(tcx, trait_def_id, pred.clone())
597+
}) {
542598
return Some(MethodViolationCode::WhereClauseReferencesSelf);
543599
}
544600

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![feature(auto_traits)]
2+
3+
pub auto trait AutoTrait {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// check-fail
2+
// aux-build: autotrait.rs
3+
4+
#![deny(where_clauses_object_safety)]
5+
6+
extern crate autotrait;
7+
8+
use autotrait::AutoTrait as NonlocalAutoTrait;
9+
10+
unsafe trait UnsafeTrait {}
11+
12+
trait Trait {
13+
fn static_lifetime_bound(&self) where Self: 'static {}
14+
15+
fn arg_lifetime_bound<'a>(&self, _arg: &'a ()) where Self: 'a {}
16+
17+
fn unsafe_trait_bound(&self) where Self: UnsafeTrait {}
18+
19+
fn nonlocal_autotrait_bound(&self) where Self: NonlocalAutoTrait {}
20+
}
21+
22+
impl Trait for () {}
23+
24+
fn main() {
25+
let trait_object = &() as &dyn Trait;
26+
trait_object.static_lifetime_bound();
27+
trait_object.arg_lifetime_bound(&());
28+
trait_object.unsafe_trait_bound(); //~ ERROR: the trait bound `dyn Trait: UnsafeTrait` is not satisfied
29+
trait_object.nonlocal_autotrait_bound(); //~ ERROR: the trait bound `dyn Trait: AutoTrait` is not satisfied
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0277]: the trait bound `dyn Trait: UnsafeTrait` is not satisfied
2+
--> $DIR/self-in-where-clause-allowed.rs:28:18
3+
|
4+
LL | trait_object.unsafe_trait_bound();
5+
| ^^^^^^^^^^^^^^^^^^ the trait `UnsafeTrait` is not implemented for `dyn Trait`
6+
|
7+
note: required by a bound in `Trait::unsafe_trait_bound`
8+
--> $DIR/self-in-where-clause-allowed.rs:17:46
9+
|
10+
LL | fn unsafe_trait_bound(&self) where Self: UnsafeTrait {}
11+
| ^^^^^^^^^^^ required by this bound in `Trait::unsafe_trait_bound`
12+
13+
error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied
14+
--> $DIR/self-in-where-clause-allowed.rs:29:18
15+
|
16+
LL | trait_object.nonlocal_autotrait_bound();
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait`
18+
|
19+
note: required by a bound in `Trait::nonlocal_autotrait_bound`
20+
--> $DIR/self-in-where-clause-allowed.rs:19:52
21+
|
22+
LL | fn nonlocal_autotrait_bound(&self) where Self: NonlocalAutoTrait {}
23+
| ^^^^^^^^^^^^^^^^^ required by this bound in `Trait::nonlocal_autotrait_bound`
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// check-fail
2+
3+
#![deny(where_clauses_object_safety)]
4+
5+
unsafe trait UnsafeTrait<T: ?Sized> {}
6+
7+
trait Trait {
8+
fn unsafe_trait_bound(&self) where (): UnsafeTrait<Self> {} //~ ERROR: the trait `Trait` cannot be made into an object
9+
//~^ WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
10+
}
11+
12+
fn main() {
13+
let _: &dyn Trait;
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error: the trait `Trait` cannot be made into an object
2+
--> $DIR/self-in-where-clause-future-compat.rs:8:8
3+
|
4+
LL | fn unsafe_trait_bound(&self) where (): UnsafeTrait<Self> {}
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8+
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
9+
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
10+
--> $DIR/self-in-where-clause-future-compat.rs:8:8
11+
|
12+
LL | trait Trait {
13+
| ----- this trait cannot be made into an object...
14+
LL | fn unsafe_trait_bound(&self) where (): UnsafeTrait<Self> {}
15+
| ^^^^^^^^^^^^^^^^^^ ...because method `unsafe_trait_bound` references the `Self` type in its `where` clause
16+
= help: consider moving `unsafe_trait_bound` to another trait
17+
note: the lint level is defined here
18+
--> $DIR/self-in-where-clause-future-compat.rs:3:9
19+
|
20+
LL | #![deny(where_clauses_object_safety)]
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
22+
23+
error: aborting due to previous error
24+

0 commit comments

Comments
 (0)