diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5c478b96fe6..7d381d8902a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -115,6 +115,28 @@ fn do_orphan_check_impl<'tcx>( // impl MyAuto for dyn Trait {} // NOT OKAY // impl MyAuto for T {} // NOT OKAY // + // With this restriction, it's guaranteed that an auto-trait is + // implemented for a trait object if and only if the auto-trait is one + // of the trait object's trait bounds (or a supertrait of a bound). In + // other words `dyn Trait + AutoTrait` always implements AutoTrait, + // while `dyn Trait` never implements AutoTrait. + // + // This is necessary in order for autotrait bounds on methods of trait + // objects to be sound. + // + // auto trait AutoTrait {} + // + // trait ObjectSafeTrait { + // fn f(&self) where Self: AutoTrait; + // } + // + // We can allow f to be called on `dyn ObjectSafeTrait + AutoTrait`. + // + // If we didn't deny `impl AutoTrait for dyn Trait`, it would be unsound + // for the ObjectSafeTrait shown above to be object safe because someone + // could take some type implementing ObjectSafeTrait but not AutoTrait, + // unsize it to `dyn ObjectSafeTrait`, and call .f() which has no + // concrete implementation (issue #50781). enum LocalImpl { Allow, Disallow { problematic_kind: &'static str }, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 565cfca9090..8f548acfd2e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -547,16 +547,56 @@ 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; + } + + // dyn Trait is okay: + // + // auto trait AutoTrait {} + // + // trait Trait { + // fn f(&self) where Self: AutoTrait; + // } + // + // because `impl AutoTrait for dyn Trait` is disallowed by coherence. + // Traits with a default impl are implemented for a trait object if and + // only if the autotrait is one of the trait object's trait bounds, like + // in `dyn Trait + AutoTrait`. This guarantees that trait objects only + // implement auto traits if the underlying type does as well. + 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 + && tcx.trait_is_auto(pred_trait_ref.def_id) + { + // Consider bounds like `Self: Bound`. Auto traits are not + // allowed to have generic parameters so `auto trait Bound {}` + // would already have reported an error at the definition of the + // auto trait. + if pred_trait_ref.substs.len() != 1 { + tcx.sess.diagnostic().delay_span_bug( + span, + "auto traits cannot have generic parameters", + ); + } + return false; + } + + contains_illegal_self_type_reference(tcx, trait_def_id, pred.clone()) + }) { return Some(MethodViolationCode::WhereClauseReferencesSelf); } 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 00000000000..6cf5ed2e46a --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.rs @@ -0,0 +1,23 @@ +// check-fail + +#![feature(auto_traits)] +#![deny(where_clauses_object_safety)] + +auto trait AutoTrait {} + +trait Trait { + fn static_lifetime_bound(&self) where Self: 'static {} + + fn arg_lifetime_bound<'a>(&self, _arg: &'a ()) where Self: 'a {} + + fn autotrait_bound(&self) where Self: AutoTrait {} +} + +impl Trait for () {} + +fn main() { + let trait_object = &() as &dyn Trait; + trait_object.static_lifetime_bound(); + trait_object.arg_lifetime_bound(&()); + trait_object.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 00000000000..ea51f5084f8 --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied + --> $DIR/self-in-where-clause-allowed.rs:22:18 + | +LL | trait_object.autotrait_bound(); + | ^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait` + | +note: required by a bound in `Trait::autotrait_bound` + --> $DIR/self-in-where-clause-allowed.rs:13:43 + | +LL | fn autotrait_bound(&self) where Self: AutoTrait {} + | ^^^^^^^^^ required by this bound in `Trait::autotrait_bound` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.