From 964fb67a5fa1b99add4efb01a7dc2a02add4b071 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Apr 2023 03:56:36 +0000 Subject: [PATCH 1/3] Use fulfillment to check Drop impl compatibility --- .../rustc_hir_analysis/src/check/dropck.rs | 324 +++++------------- .../src/infer/lexical_region_resolve/mod.rs | 11 + compiler/rustc_middle/src/traits/mod.rs | 4 + .../src/traits/error_reporting/suggestions.rs | 3 +- 4 files changed, 108 insertions(+), 234 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index bae80807f71b9..5ba1ca1c807bc 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -1,12 +1,14 @@ // FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. // // We don't do any drop checking during hir typeck. +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{struct_span_err, ErrorGuaranteed}; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::util::IgnoreRegions; -use rustc_middle::ty::{self, Predicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_trait_selection::traits::{self, ObligationCtxt}; use crate::errors; use crate::hir::def_id::{DefId, LocalDefId}; @@ -43,21 +45,20 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro } } let dtor_self_type = tcx.type_of(drop_impl_did).subst_identity(); - let dtor_predicates = tcx.predicates_of(drop_impl_did); match dtor_self_type.kind() { - ty::Adt(adt_def, self_to_impl_substs) => { + ty::Adt(adt_def, adt_to_impl_substs) => { ensure_drop_params_and_item_params_correspond( tcx, drop_impl_did.expect_local(), adt_def.did(), - self_to_impl_substs, + adt_to_impl_substs, )?; ensure_drop_predicates_are_implied_by_item_defn( tcx, - dtor_predicates, + drop_impl_did.expect_local(), adt_def.did().expect_local(), - self_to_impl_substs, + adt_to_impl_substs, ) } _ => { @@ -78,9 +79,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( tcx: TyCtxt<'tcx>, drop_impl_did: LocalDefId, self_type_did: DefId, - drop_impl_substs: SubstsRef<'tcx>, + adt_to_impl_substs: SubstsRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else { + let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, IgnoreRegions::No) else { return Ok(()) }; @@ -111,237 +112,94 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( /// implied by assuming the predicates attached to self_type_did. fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( tcx: TyCtxt<'tcx>, - dtor_predicates: ty::GenericPredicates<'tcx>, - self_type_did: LocalDefId, - self_to_impl_substs: SubstsRef<'tcx>, + drop_impl_def_id: LocalDefId, + adt_def_id: LocalDefId, + adt_to_impl_substs: SubstsRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let mut result = Ok(()); - - // Here is an example, analogous to that from - // `compare_impl_method`. - // - // Consider a struct type: - // - // struct Type<'c, 'b:'c, 'a> { - // x: &'a Contents // (contents are irrelevant; - // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.) - // } - // - // and a Drop impl: - // - // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> { - // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y) - // } - // - // We start out with self_to_impl_substs, that maps the generic - // parameters of Type to that of the Drop impl. + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + // Take the param-env of the adt and substitute the substs that show up in + // the implementation's self type. This gives us the assumptions that the + // self ty of the implementation is allowed to know just from it being a + // well-formed adt, since that's all we're allowed to assume while proving + // the Drop implementation is not specialized. // - // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x} - // - // Applying this to the predicates (i.e., assumptions) provided by the item - // definition yields the instantiated assumptions: - // - // ['y : 'z] - // - // We then check all of the predicates of the Drop impl: - // - // ['y:'z, 'x:'y] - // - // and ensure each is in the list of instantiated - // assumptions. Here, `'y:'z` is present, but `'x:'y` is - // absent. So we report an error that the Drop impl injected a - // predicate that is not present on the struct definition. - - // We can assume the predicates attached to struct/enum definition - // hold. - let generic_assumptions = tcx.predicates_of(self_type_did); - - let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); - let assumptions_in_impl_context = assumptions_in_impl_context.predicates; - - debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates); - - let self_param_env = tcx.param_env(self_type_did); - - // An earlier version of this code attempted to do this checking - // via the traits::fulfill machinery. However, it ran into trouble - // since the fulfill machinery merely turns outlives-predicates - // 'a:'b and T:'b into region inference constraints. It is simpler - // just to look for all the predicates directly. - - assert_eq!(dtor_predicates.parent, None); - for &(predicate, predicate_sp) in dtor_predicates.predicates { - // (We do not need to worry about deep analysis of type - // expressions etc because the Drop impls are already forced - // to take on a structure that is roughly an alpha-renaming of - // the generic parameters of the item definition.) - - // This path now just checks *all* predicates via an instantiation of - // the `SimpleEqRelation`, which simply forwards to the `relate` machinery - // after taking care of anonymizing late bound regions. - // - // However, it may be more efficient in the future to batch - // the analysis together via the fulfill (see comment above regarding - // the usage of the fulfill machinery), rather than the - // repeated `.iter().any(..)` calls. + // We don't need to normalize this param-env or anything, since we're only + // substituting it with free params, so no additional param-env normalization + // can occur on top of what has been done in the param_env query itself. + let param_env = ty::EarlyBinder(tcx.param_env(adt_def_id)) + .subst(tcx, adt_to_impl_substs) + .with_constness(tcx.constness(drop_impl_def_id)); + + for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) { + let normalize_cause = traits::ObligationCause::misc(span, adt_def_id); + let pred = ocx.normalize(&normalize_cause, param_env, pred); + let cause = traits::ObligationCause::new(span, adt_def_id, traits::DropImpl); + ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred)); + } - // This closure is a more robust way to check `Predicate` equality - // than simple `==` checks (which were the previous implementation). - // It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`, - // `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait), - // while delegating on simple equality for the other `Predicate`. - // This implementation solves (Issue #59497) and (Issue #58311). - // It is unclear to me at the moment whether the approach based on `relate` - // could be extended easily also to the other `Predicate`. - let predicate_matches_closure = |p: Predicate<'tcx>| { - let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); - let predicate = predicate.kind(); - let p = p.kind(); - match (predicate.skip_binder(), p.skip_binder()) { - ( - ty::PredicateKind::Clause(ty::Clause::Trait(a)), - ty::PredicateKind::Clause(ty::Clause::Trait(b)), - ) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(), - ( - ty::PredicateKind::Clause(ty::Clause::Projection(a)), - ty::PredicateKind::Clause(ty::Clause::Projection(b)), - ) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(), - ( - ty::PredicateKind::ConstEvaluatable(a), - ty::PredicateKind::ConstEvaluatable(b), - ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), - ( - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( - ty_a, - lt_a, - ))), - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( - ty_b, - lt_b, - ))), - ) => { - relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok() - && relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok() - } - (ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => { - relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok() - } - _ => predicate == p, + // All of the custom error reporting logic is to preserve parity with the old + // error messages. + // + // They can probably get removed with better treatment of the new `DropImpl` + // obligation cause code, and perhaps some custom logic in `report_region_errors`. + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let mut guar = None; + let mut root_predicates = FxHashSet::default(); + for error in errors { + let root_predicate = error.root_obligation.predicate; + if root_predicates.insert(root_predicate) { + let item_span = tcx.def_span(adt_def_id); + let self_descr = tcx.def_descr(adt_def_id.to_def_id()); + guar = Some( + struct_span_err!( + tcx.sess, + error.root_obligation.cause.span, + E0367, + "`Drop` impl requires `{root_predicate}` \ + but the {self_descr} it is implemented for does not", + ) + .span_note(item_span, "the implementor must specify the same requirement") + .emit(), + ); } - }; - - if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_descr(self_type_did.to_def_id()); - let reported = struct_span_err!( - tcx.sess, - predicate_sp, - E0367, - "`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not", - ) - .span_note(item_span, "the implementor must specify the same requirement") - .emit(); - result = Err(reported); } + return Err(guar.unwrap()); } - result -} - -/// This is an implementation of the [`TypeRelation`] trait with the -/// aim of simply comparing for equality (without side-effects). -/// -/// It is not intended to be used anywhere else other than here. -pub(crate) struct SimpleEqRelation<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> SimpleEqRelation<'tcx> { - fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> { - SimpleEqRelation { tcx, param_env } - } -} - -impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } - - fn tag(&self) -> &'static str { - "dropck::SimpleEqRelation" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn relate_with_variance>( - &mut self, - _: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - // Here we ignore variance because we require drop impl's types - // to be *exactly* the same as to the ones in the struct definition. - self.relate(a, b) - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b); - ty::relate::super_relate_tys(self, a, b) - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b); - - // We can just equate the regions because LBRs have been - // already anonymized. - if a == b { - Ok(a) - } else { - // I'm not sure is this `TypeError` is the right one, but - // it should not matter as it won't be checked (the dropck - // will emit its own, more informative and higher-level errors - // in case anything goes wrong). - Err(TypeError::RegionsPlaceholderMismatch) + let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env)); + if !errors.is_empty() { + let mut guar = None; + for error in errors { + let item_span = tcx.def_span(adt_def_id); + let self_descr = tcx.def_descr(adt_def_id.to_def_id()); + let outlives = match error { + RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"), + RegionResolutionError::GenericBoundFailure(_, generic, r) => { + format!("{generic}: {r}") + } + RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"), + RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => { + format!("{b}: {a}", a = tcx.mk_re_var(a)) + } + }; + guar = Some( + struct_span_err!( + tcx.sess, + error.origin().span(), + E0367, + "`Drop` impl requires `{outlives}` \ + but the {self_descr} it is implemented for does not", + ) + .span_note(item_span, "the implementor must specify the same requirement") + .emit(), + ); } + return Err(guar.unwrap()); } - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b); - ty::relate::super_relate_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - debug!("SimpleEqRelation::binders({:?}: {:?}", a, b); - - // Anonymizing the LBRs is necessary to solve (Issue #59497). - // After we do so, it should be totally fine to skip the binders. - let anon_a = self.tcx.anonymize_bound_vars(a); - let anon_b = self.tcx.anonymize_bound_vars(b); - self.relate(anon_a.skip_binder(), anon_b.skip_binder())?; - - Ok(a) - } + Ok(()) } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index f1468cae455b5..8482ae2aa38c8 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -102,6 +102,17 @@ pub enum RegionResolutionError<'tcx> { ), } +impl<'tcx> RegionResolutionError<'tcx> { + pub fn origin(&self) -> &SubregionOrigin<'tcx> { + match self { + RegionResolutionError::ConcreteFailure(origin, _, _) + | RegionResolutionError::GenericBoundFailure(origin, _, _) + | RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _) + | RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin, + } + } +} + struct RegionAndOrigin<'tcx> { region: Region<'tcx>, origin: SubregionOrigin<'tcx>, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index c237556420841..8366567c2c364 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -444,6 +444,10 @@ pub enum ObligationCauseCode<'tcx> { AscribeUserTypeProvePredicate(Span), RustCall, + + /// Obligations to prove that a `std::ops::Drop` impl is not stronger than + /// the ADT it's being implemented for. + DropImpl, } /// The 'location' at which we try to perform HIR-based wf checking. diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index d34eb193453bd..37beb31a7a4c0 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2790,7 +2790,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::LetElse | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) - | ObligationCauseCode::RustCall => {} + | ObligationCauseCode::RustCall + | ObligationCauseCode::DropImpl => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } From 9d44f9b4e27bbb83108cde6e3dfffbeca7d4e71e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Apr 2023 04:04:25 +0000 Subject: [PATCH 2/3] Add test for #110557 --- .../drop-impl-pred.no.stderr | 24 ++++++++++++++++++ .../non_lifetime_binders/drop-impl-pred.rs | 25 +++++++++++++++++++ .../drop-impl-pred.yes.stderr | 11 ++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/ui/traits/non_lifetime_binders/drop-impl-pred.no.stderr create mode 100644 tests/ui/traits/non_lifetime_binders/drop-impl-pred.rs create mode 100644 tests/ui/traits/non_lifetime_binders/drop-impl-pred.yes.stderr diff --git a/tests/ui/traits/non_lifetime_binders/drop-impl-pred.no.stderr b/tests/ui/traits/non_lifetime_binders/drop-impl-pred.no.stderr new file mode 100644 index 0000000000000..a985b1a6e12f6 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/drop-impl-pred.no.stderr @@ -0,0 +1,24 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/drop-impl-pred.rs:6:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0367]: `Drop` impl requires `H: Foo` but the struct it is implemented for does not + --> $DIR/drop-impl-pred.rs:19:15 + | +LL | for H: Foo, + | ^^^ + | +note: the implementor must specify the same requirement + --> $DIR/drop-impl-pred.rs:12:1 + | +LL | struct Bar(T) where T: Foo; + | ^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/traits/non_lifetime_binders/drop-impl-pred.rs b/tests/ui/traits/non_lifetime_binders/drop-impl-pred.rs new file mode 100644 index 0000000000000..c65b5ea9ba493 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/drop-impl-pred.rs @@ -0,0 +1,25 @@ +// revisions: no yes +//[yes] check-pass + +// Issue 110557 + +#![feature(non_lifetime_binders)] +//~^ WARN the feature `non_lifetime_binders` is incomplete + +pub trait Foo {} + +#[cfg(no)] +struct Bar(T) where T: Foo; + +#[cfg(yes)] +struct Bar(T) where for H: Foo; + +impl Drop for Bar +where + for H: Foo, +//[no]~^ ERROR `Drop` impl requires `H: Foo` but the struct it is implemented for does not +{ + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/drop-impl-pred.yes.stderr b/tests/ui/traits/non_lifetime_binders/drop-impl-pred.yes.stderr new file mode 100644 index 0000000000000..165cf2ee13da8 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/drop-impl-pred.yes.stderr @@ -0,0 +1,11 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/drop-impl-pred.rs:6:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + From 2e346b6f3f3be75d0e0b536a6a8cf2f82241b3b4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 21 Apr 2023 18:04:39 +0000 Subject: [PATCH 3/3] Even more tests --- .../dropck/explicit-drop-bounds.bad1.stderr | 35 +++++++++++++++ .../dropck/explicit-drop-bounds.bad2.stderr | 35 +++++++++++++++ tests/ui/dropck/explicit-drop-bounds.rs | 44 +++++++++++++++++++ .../explicit-implied-outlives.bad1.stderr | 15 +++++++ .../explicit-implied-outlives.bad2.stderr | 15 +++++++ tests/ui/dropck/explicit-implied-outlives.rs | 43 ++++++++++++++++++ tests/ui/dropck/transitive-outlives-2.rs | 18 ++++++++ .../ui/dropck/transitive-outlives.bad.stderr | 15 +++++++ tests/ui/dropck/transitive-outlives.rs | 26 +++++++++++ tests/ui/dropck/trivial-impl-bounds.rs | 34 ++++++++++++++ 10 files changed, 280 insertions(+) create mode 100644 tests/ui/dropck/explicit-drop-bounds.bad1.stderr create mode 100644 tests/ui/dropck/explicit-drop-bounds.bad2.stderr create mode 100644 tests/ui/dropck/explicit-drop-bounds.rs create mode 100644 tests/ui/dropck/explicit-implied-outlives.bad1.stderr create mode 100644 tests/ui/dropck/explicit-implied-outlives.bad2.stderr create mode 100644 tests/ui/dropck/explicit-implied-outlives.rs create mode 100644 tests/ui/dropck/transitive-outlives-2.rs create mode 100644 tests/ui/dropck/transitive-outlives.bad.stderr create mode 100644 tests/ui/dropck/transitive-outlives.rs create mode 100644 tests/ui/dropck/trivial-impl-bounds.rs diff --git a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr new file mode 100644 index 0000000000000..3b506c7e7ec13 --- /dev/null +++ b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:27:18 + | +LL | impl Drop for DropMe + | ^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider further restricting type parameter `T` + | +LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` + | ~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:32:13 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider further restricting type parameter `T` + | +LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` + | ~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr new file mode 100644 index 0000000000000..832af3e521a9d --- /dev/null +++ b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:37:18 + | +LL | impl Drop for DropMe + | ^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider restricting type parameter `T` + | +LL | impl Drop for DropMe + | +++++++++++++++++++ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:40:13 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider restricting type parameter `T` + | +LL | impl Drop for DropMe + | +++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.rs b/tests/ui/dropck/explicit-drop-bounds.rs new file mode 100644 index 0000000000000..ab6f33c09994e --- /dev/null +++ b/tests/ui/dropck/explicit-drop-bounds.rs @@ -0,0 +1,44 @@ +// revisions: good1 good2 bad1 bad2 +//[good1] check-pass +//[good2] check-pass + +use std::ops::Drop; + +struct DropMe(T); + +#[cfg(good1)] +impl Drop for DropMe +where + T: Copy + Clone, +{ + fn drop(&mut self) {} +} + +#[cfg(good2)] +impl Drop for DropMe +where + T: Copy, + [T; 1]: Copy, // Trivial bound implied by `T: Copy` +{ + fn drop(&mut self) {} +} + +#[cfg(bad1)] +impl Drop for DropMe +//[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied +where + [T; 1]: Copy, // But `[T; 1]: Copy` does not imply `T: Copy` +{ + fn drop(&mut self) {} + //[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied +} + +#[cfg(bad2)] +impl Drop for DropMe +//[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied +{ + fn drop(&mut self) {} + //[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied +} + +fn main() {} diff --git a/tests/ui/dropck/explicit-implied-outlives.bad1.stderr b/tests/ui/dropck/explicit-implied-outlives.bad1.stderr new file mode 100644 index 0000000000000..bf6d70e7d3758 --- /dev/null +++ b/tests/ui/dropck/explicit-implied-outlives.bad1.stderr @@ -0,0 +1,15 @@ +error[E0367]: `Drop` impl requires `T: 'static` but the struct it is implemented for does not + --> $DIR/explicit-implied-outlives.rs:28:8 + | +LL | T: 'static, + | ^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/explicit-implied-outlives.rs:7:1 + | +LL | struct DropMe<'a, T>(&'a T); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/dropck/explicit-implied-outlives.bad2.stderr b/tests/ui/dropck/explicit-implied-outlives.bad2.stderr new file mode 100644 index 0000000000000..27a15170bddb5 --- /dev/null +++ b/tests/ui/dropck/explicit-implied-outlives.bad2.stderr @@ -0,0 +1,15 @@ +error[E0367]: `Drop` impl requires `'a: 'static` but the struct it is implemented for does not + --> $DIR/explicit-implied-outlives.rs:37:9 + | +LL | 'a: 'static, + | ^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/explicit-implied-outlives.rs:7:1 + | +LL | struct DropMe<'a, T>(&'a T); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/dropck/explicit-implied-outlives.rs b/tests/ui/dropck/explicit-implied-outlives.rs new file mode 100644 index 0000000000000..fa446591f3dc4 --- /dev/null +++ b/tests/ui/dropck/explicit-implied-outlives.rs @@ -0,0 +1,43 @@ +// revisions: good1 good2 bad1 bad2 +//[good1] check-pass +//[good2] check-pass + +use std::ops::Drop; + +struct DropMe<'a, T>(&'a T); + +#[cfg(good1)] +impl<'a, T> Drop for DropMe<'a, T> +where + T: 'a, // Implied by struct, explicit on impl +{ + fn drop(&mut self) {} +} + +#[cfg(good2)] +impl<'a, T> Drop for DropMe<'a, T> +where + 'static: 'a, // Trivial bound +{ + fn drop(&mut self) {} +} + +#[cfg(bad1)] +impl<'a, T> Drop for DropMe<'a, T> +where + T: 'static, + //[bad1]~^ ERROR `Drop` impl requires `T: 'static` +{ + fn drop(&mut self) {} +} + +#[cfg(bad2)] +impl<'a, T> Drop for DropMe<'a, T> +where + 'a: 'static, + //[bad2]~^ ERROR `Drop` impl requires `'a: 'static` +{ + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/transitive-outlives-2.rs b/tests/ui/dropck/transitive-outlives-2.rs new file mode 100644 index 0000000000000..87154e25d4091 --- /dev/null +++ b/tests/ui/dropck/transitive-outlives-2.rs @@ -0,0 +1,18 @@ +// check-pass + +use std::marker::PhantomData; +use std::ops::Drop; + +// a >= b >= c >= a implies a = b = c +struct DropMe<'a: 'b, 'b: 'c, 'c: 'a>( + PhantomData<&'a ()>, + PhantomData<&'b ()>, + PhantomData<&'c ()>, +); + +// a >= b, a >= c, b >= a, c >= a implies a = b = c +impl<'a: 'b + 'c, 'b: 'a, 'c: 'a> Drop for DropMe<'a, 'b, 'c> { + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/transitive-outlives.bad.stderr b/tests/ui/dropck/transitive-outlives.bad.stderr new file mode 100644 index 0000000000000..da5088b27b414 --- /dev/null +++ b/tests/ui/dropck/transitive-outlives.bad.stderr @@ -0,0 +1,15 @@ +error[E0367]: `Drop` impl requires `'a: 'c` but the struct it is implemented for does not + --> $DIR/transitive-outlives.rs:20:9 + | +LL | 'a: 'c, + | ^^ + | +note: the implementor must specify the same requirement + --> $DIR/transitive-outlives.rs:7:1 + | +LL | struct DropMe<'a, 'b: 'a, 'c: 'b>(PhantomData<&'a ()>, PhantomData<&'b ()>, PhantomData<&'c ()>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/dropck/transitive-outlives.rs b/tests/ui/dropck/transitive-outlives.rs new file mode 100644 index 0000000000000..d071664abdeb6 --- /dev/null +++ b/tests/ui/dropck/transitive-outlives.rs @@ -0,0 +1,26 @@ +// revisions: good bad +//[good] check-pass + +use std::marker::PhantomData; +use std::ops::Drop; + +struct DropMe<'a, 'b: 'a, 'c: 'b>(PhantomData<&'a ()>, PhantomData<&'b ()>, PhantomData<&'c ()>); + +#[cfg(good)] +impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c> +where + 'c: 'a, +{ + fn drop(&mut self) {} +} + +#[cfg(bad)] +impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c> +where + 'a: 'c, + //[bad]~^ ERROR `Drop` impl requires `'a: 'c` +{ + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/trivial-impl-bounds.rs b/tests/ui/dropck/trivial-impl-bounds.rs new file mode 100644 index 0000000000000..a8f5d2c354bc9 --- /dev/null +++ b/tests/ui/dropck/trivial-impl-bounds.rs @@ -0,0 +1,34 @@ +// revisions: good1 good2 good3 +// check-pass + +use std::ops::Drop; + +struct Foo; + +const X: usize = 1; + +#[cfg(good1)] +impl Drop for Foo +where + [(); X]:, // Trivial WF bound +{ + fn drop(&mut self) {} +} + +#[cfg(good2)] +impl Drop for Foo +where + for<'a> &'a (): Copy, // Trivial trait bound +{ + fn drop(&mut self) {} +} + +#[cfg(good3)] +impl Drop for Foo +where + for<'a> &'a (): 'a, // Trivial outlives bound +{ + fn drop(&mut self) {} +} + +fn main() {}