diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 13345183124..f1c7d3035b9 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -988,14 +988,12 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { let mut err_span = self.span; - // Check to see if the type of this place can ever have a drop impl. If not, this - // `Drop` terminator is frivolous. - let ty_needs_drop = dropped_place - .ty(self.body, self.tcx) - .ty - .needs_non_const_drop(self.tcx, self.param_env); + let ty_needs_non_const_drop = qualifs::NeedsNonConstDrop::in_any_value_of_ty( + self.ccx, + dropped_place.ty(self.body, self.tcx).ty, + ); - if !ty_needs_drop { + if !ty_needs_non_const_drop { return; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 50b691dffe8..ea8f0a29181 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -3,10 +3,14 @@ //! See the `Qualif` trait for more info. use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; -use rustc_trait_selection::traits; +use rustc_trait_selection::traits::{ + self, ImplSource, Obligation, ObligationCause, SelectionContext, +}; use super::ConstCx; @@ -108,7 +112,28 @@ impl Qualif for NeedsNonConstDrop { } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.needs_drop(cx.tcx, cx.param_env) + let trait_ref = ty::TraitRef { + def_id: cx.tcx.require_lang_item(hir::LangItem::Drop, None), + substs: cx.tcx.mk_substs_trait(ty, &[]), + }; + let obligation = Obligation::new( + ObligationCause::dummy(), + cx.param_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::ConstIfConst, + }), + ); + + let implsrc = cx.tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::with_constness(&infcx, hir::Constness::Const); + selcx.select(&obligation) + }); + match implsrc { + Ok(Some(ImplSource::ConstDrop(_))) + | Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => false, + _ => true, + } } fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index ffaef566cd5..6d64dc8254b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -283,6 +283,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.is_in_const_context { self.assemble_const_drop_candidates(obligation, &mut candidates)?; } else { + debug!("passing ~const Drop bound; in non-const context"); // `~const Drop` when we are not in a const context has no effect. candidates.vec.push(ConstDropCandidate) } @@ -821,6 +822,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)]; while let Some((ty, depth)) = stack.pop() { + let mut noreturn = false; + self.check_recursion_depth(depth, obligation)?; let mut copy_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; let mut copy_obligation = @@ -836,8 +839,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let copy_conditions = self.copy_clone_conditions(©_obligation); self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates); if !copy_candidates.vec.is_empty() { - continue; + noreturn = true; } + debug!(?copy_candidates.vec, "assemble_const_drop_candidates - copy"); match ty.kind() { ty::Int(_) @@ -857,22 +861,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Adt(def, subst) => { let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - self.assemble_candidates_from_impls(obligation, &mut set); - if set - .vec - .into_iter() - .find(|candidate| { - if let SelectionCandidate::ImplCandidate(did) = candidate { - matches!(self.tcx().impl_constness(*did), hir::Constness::NotConst) - } else { - false - } - }) - .is_none() - { - // could not find a const impl for Drop, iterate over its fields. - stack - .extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1))); + self.assemble_candidates_from_impls( + &obligation.with(obligation.predicate.map_bound(|mut pred| { + pred.trait_ref.substs = self.tcx().mk_substs_trait(ty, &[]); + pred + })), + &mut set, + ); + stack.extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1))); + + debug!(?set.vec, "assemble_const_drop_candidates - ty::Adt"); + if set.vec.into_iter().any(|candidate| { + if let SelectionCandidate::ImplCandidate(did) = candidate { + matches!(self.tcx().impl_constness(did), hir::Constness::NotConst) + } else { + false + } + }) { + if !noreturn { + // has non-const Drop + return Ok(()); + } + debug!("not returning"); } } @@ -903,8 +913,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(_) | ty::Placeholder(_) | ty::Projection(..) - | ty::Param(..) => return Ok(()), + | ty::Param(..) => { + if !noreturn { + return Ok(()); + } + debug!("not returning"); + } } + debug!(?stack, "assemble_const_drop_candidates - in loop"); } // all types have passed. candidates.vec.push(ConstDropCandidate); diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs index a79b67fb0db..17442e1b05a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs @@ -16,16 +16,19 @@ impl const Drop for ConstImplWithDropGlue { fn drop(&mut self) {} } -const fn check() {} +const fn check(_: T) {} macro_rules! check_all { - ($($T:ty),*$(,)?) => {$( - const _: () = check::<$T>(); + ($($exp:expr),*$(,)?) => {$( + const _: () = check($exp); )*}; } check_all! { - ConstImplWithDropGlue, + NonTrivialDrop, + //~^ ERROR the trait bound + ConstImplWithDropGlue(NonTrivialDrop), + //~^ ERROR the trait bound } fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr new file mode 100644 index 00000000000..e962503d7df --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied + --> $DIR/const-drop-fail.rs:28:5 + | +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:19:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied + --> $DIR/const-drop-fail.rs:30:5 + | +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:19:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`.