Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let unsafe traits and autotraits be used as bound in dyn-safe trait #106604

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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