From ba9b196492ccec2e15e4b7106adbd0f9fb9871cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 19 Jan 2023 01:24:45 -0800 Subject: [PATCH] Autotrait bounds on dyn-safe trait methods --- .../src/coherence/orphan.rs | 22 ++++++++ .../src/traits/object_safety.rs | 52 +++++++++++++++---- .../self-in-where-clause-allowed.rs | 23 ++++++++ .../self-in-where-clause-allowed.stderr | 15 ++++++ 4 files changed, 102 insertions(+), 10 deletions(-) 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 diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f06612059b84c..c434bfaa2ac3d 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -116,6 +116,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). let problematic_kind = match self_ty_kind { ty::Dynamic(..) => Some("trait object"), ty::Param(..) if !self_ty.is_sized(tcx, tcx.param_env(def_id)) => { diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index c9121212cd8f1..ac2296229d7c2 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -529,16 +529,48 @@ 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`. + 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) + { + // 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/self-in-where-clause-allowed.rs b/tests/ui/where-clauses/self-in-where-clause-allowed.rs new file mode 100644 index 0000000000000..6cf5ed2e46aec --- /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 0000000000000..ea51f5084f875 --- /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`.