From 18a88be00c3222084181d34b8ae24f839774fd05 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 8 Jan 2023 11:42:53 -0800 Subject: [PATCH] Let unsafe traits and autotraits be used as bound in dyn-safe trait --- .../src/traits/object_safety.rs | 76 ++++++++++++++++--- tests/ui/where-clauses/auxiliary/autotrait.rs | 3 + .../self-in-where-clause-allowed.rs | 30 ++++++++ .../self-in-where-clause-allowed.stderr | 27 +++++++ .../self-in-where-clause-future-compat.rs | 14 ++++ .../self-in-where-clause-future-compat.stderr | 24 ++++++ 6 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 tests/ui/where-clauses/auxiliary/autotrait.rs create mode 100644 tests/ui/where-clauses/self-in-where-clause-allowed.rs create mode 100644 tests/ui/where-clauses/self-in-where-clause-allowed.stderr create mode 100644 tests/ui/where-clauses/self-in-where-clause-future-compat.rs create mode 100644 tests/ui/where-clauses/self-in-where-clause-future-compat.stderr diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 8b1ced78f4e8a..45e499712cf45 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -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` 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); } diff --git a/tests/ui/where-clauses/auxiliary/autotrait.rs b/tests/ui/where-clauses/auxiliary/autotrait.rs new file mode 100644 index 0000000000000..13d09cddd2a9d --- /dev/null +++ b/tests/ui/where-clauses/auxiliary/autotrait.rs @@ -0,0 +1,3 @@ +#![feature(auto_traits)] + +pub auto trait AutoTrait {} diff --git a/tests/ui/where-clauses/self-in-where-clause-allowed.rs b/tests/ui/where-clauses/self-in-where-clause-allowed.rs new file mode 100644 index 0000000000000..7f55972a83b2e --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.rs @@ -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 +} diff --git a/tests/ui/where-clauses/self-in-where-clause-allowed.stderr b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr new file mode 100644 index 0000000000000..3de293820d306 --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr @@ -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`. diff --git a/tests/ui/where-clauses/self-in-where-clause-future-compat.rs b/tests/ui/where-clauses/self-in-where-clause-future-compat.rs new file mode 100644 index 0000000000000..3d22f8c0321dd --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-future-compat.rs @@ -0,0 +1,14 @@ +// check-fail + +#![deny(where_clauses_object_safety)] + +unsafe trait UnsafeTrait {} + +trait Trait { + fn unsafe_trait_bound(&self) where (): UnsafeTrait {} //~ 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; +} diff --git a/tests/ui/where-clauses/self-in-where-clause-future-compat.stderr b/tests/ui/where-clauses/self-in-where-clause-future-compat.stderr new file mode 100644 index 0000000000000..4aba34d309090 --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-future-compat.stderr @@ -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 {} + | ^^^^^^^^^^^^^^^^^^ + | + = 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 +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 + --> $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 {} + | ^^^^^^^^^^^^^^^^^^ ...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 +