Skip to content

Commit

Permalink
Let unsafe traits and autotraits be used as bound in dyn-safe trait
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Jan 11, 2023
1 parent ef4046e commit 18a88be
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 10 deletions.
76 changes: 66 additions & 10 deletions compiler/rustc_trait_selection/src/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,16 +529,72 @@ fn virtual_call_violation_for_method<'tcx>(

// NOTE: This check happens last, because it results in a lint, and not a
// hard error.
if tcx
.predicates_of(method.def_id)
.predicates
.iter()
// A trait object can't claim to live more than the concrete type,
// so outlives predicates will always hold.
.cloned()
.filter(|(p, _)| p.to_opt_type_outlives().is_none())
.any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred))
{
if tcx.predicates_of(method.def_id).predicates.iter().any(|(pred, _span)| {
// dyn Trait is okay:
//
// trait Trait {
// fn f(&self) where Self: 'static;
// }
//
// because a trait object can't claim to live longer than the
// concrete type. If the lifetime bound holds on dyn Trait then it's
// guaranteed to hold as well on the concrete type.
if pred.to_opt_type_outlives().is_some() {
return false;
}

if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
trait_ref: pred_trait_ref,
constness: ty::BoundConstness::NotConst,
polarity: ty::ImplPolarity::Positive,
})) = pred.kind().skip_binder()
&& pred_trait_ref.self_ty() == tcx.types.self_param
{
let pred_trait_def = tcx.trait_def(pred_trait_ref.def_id);
let mut disregard_self_in_self_ty = false;

// dyn Trait is okay:
//
// unsafe trait Bound {}
//
// trait Trait {
// fn f(&self) where Self: Bound;
// }
//
// because we don't need to worry about a potential `unsafe impl
// Bound for dyn Trait`. Whoever wrote such an impl, it's their
// fault when f gets called on a !Bound concrete type.
if let hir::Unsafety::Unsafe = pred_trait_def.unsafety {
disregard_self_in_self_ty = true;
}

// dyn Trait is okay:
//
// extern crate other_crate {
// auto trait Bound {}
// }
//
// trait Trait {
// fn f(&self) where Self: other_crate::Bound;
// }
//
// because `impl Bound for dyn Trait` is impossible. Cross-crate
// traits with a default impl can only be implemented for a
// struct/enum type, not dyn Trait.
if pred_trait_def.has_auto_impl && pred_trait_def.def_id.krate != trait_def_id.krate {
disregard_self_in_self_ty = true;
}

if disregard_self_in_self_ty {
// Only check the rest of the bound's parameters. So `Self:
// Bound<Self>` is still considered illegal.
let rest_of_substs = &pred_trait_ref.substs[1..];
return contains_illegal_self_type_reference(tcx, trait_def_id, rest_of_substs);
}
}

contains_illegal_self_type_reference(tcx, trait_def_id, pred.clone())
}) {
return Some(MethodViolationCode::WhereClauseReferencesSelf);
}

Expand Down
3 changes: 3 additions & 0 deletions tests/ui/where-clauses/auxiliary/autotrait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#![feature(auto_traits)]

pub auto trait AutoTrait {}
30 changes: 30 additions & 0 deletions tests/ui/where-clauses/self-in-where-clause-allowed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// check-fail
// aux-build: autotrait.rs

#![deny(where_clauses_object_safety)]

extern crate autotrait;

use autotrait::AutoTrait as NonlocalAutoTrait;

unsafe trait UnsafeTrait {}

trait Trait {
fn static_lifetime_bound(&self) where Self: 'static {}

fn arg_lifetime_bound<'a>(&self, _arg: &'a ()) where Self: 'a {}

fn unsafe_trait_bound(&self) where Self: UnsafeTrait {}

fn nonlocal_autotrait_bound(&self) where Self: NonlocalAutoTrait {}
}

impl Trait for () {}

fn main() {
let trait_object = &() as &dyn Trait;
trait_object.static_lifetime_bound();
trait_object.arg_lifetime_bound(&());
trait_object.unsafe_trait_bound(); //~ ERROR: the trait bound `dyn Trait: UnsafeTrait` is not satisfied
trait_object.nonlocal_autotrait_bound(); //~ ERROR: the trait bound `dyn Trait: AutoTrait` is not satisfied
}
27 changes: 27 additions & 0 deletions tests/ui/where-clauses/self-in-where-clause-allowed.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0277]: the trait bound `dyn Trait: UnsafeTrait` is not satisfied
--> $DIR/self-in-where-clause-allowed.rs:28:18
|
LL | trait_object.unsafe_trait_bound();
| ^^^^^^^^^^^^^^^^^^ the trait `UnsafeTrait` is not implemented for `dyn Trait`
|
note: required by a bound in `Trait::unsafe_trait_bound`
--> $DIR/self-in-where-clause-allowed.rs:17:46
|
LL | fn unsafe_trait_bound(&self) where Self: UnsafeTrait {}
| ^^^^^^^^^^^ required by this bound in `Trait::unsafe_trait_bound`

error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied
--> $DIR/self-in-where-clause-allowed.rs:29:18
|
LL | trait_object.nonlocal_autotrait_bound();
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait`
|
note: required by a bound in `Trait::nonlocal_autotrait_bound`
--> $DIR/self-in-where-clause-allowed.rs:19:52
|
LL | fn nonlocal_autotrait_bound(&self) where Self: NonlocalAutoTrait {}
| ^^^^^^^^^^^^^^^^^ required by this bound in `Trait::nonlocal_autotrait_bound`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
14 changes: 14 additions & 0 deletions tests/ui/where-clauses/self-in-where-clause-future-compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// check-fail

#![deny(where_clauses_object_safety)]

unsafe trait UnsafeTrait<T: ?Sized> {}

trait Trait {
fn unsafe_trait_bound(&self) where (): UnsafeTrait<Self> {} //~ ERROR: the trait `Trait` cannot be made into an object
//~^ WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
}

fn main() {
let _: &dyn Trait;
}
24 changes: 24 additions & 0 deletions tests/ui/where-clauses/self-in-where-clause-future-compat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error: the trait `Trait` cannot be made into an object
--> $DIR/self-in-where-clause-future-compat.rs:8:8
|
LL | fn unsafe_trait_bound(&self) where (): UnsafeTrait<Self> {}
| ^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
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>
--> $DIR/self-in-where-clause-future-compat.rs:8:8
|
LL | trait Trait {
| ----- this trait cannot be made into an object...
LL | fn unsafe_trait_bound(&self) where (): UnsafeTrait<Self> {}
| ^^^^^^^^^^^^^^^^^^ ...because method `unsafe_trait_bound` references the `Self` type in its `where` clause
= help: consider moving `unsafe_trait_bound` to another trait
note: the lint level is defined here
--> $DIR/self-in-where-clause-future-compat.rs:3:9
|
LL | #![deny(where_clauses_object_safety)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

0 comments on commit 18a88be

Please sign in to comment.