From f9eb0b3b7ba13ea80733f418ac28e8988d60588d Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Fri, 14 Jan 2022 21:01:20 -0800 Subject: [PATCH 1/8] ignore constness when checking dtor predicates --- compiler/rustc_typeck/src/check/dropck.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index c8986aa7f53b0..46610a0e08db6 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -228,9 +228,15 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let predicate = predicate.kind(); let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { - (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { - relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() - } + (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => relator + .relate( + predicate.rebind(ty::TraitPredicate { + constness: ty::BoundConstness::NotConst, + ..a + }), + p.rebind(b), + ) + .is_ok(), (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } From 6ed42a7ca48f11c6d2e381af033ccbf0f9523a64 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 18 Jan 2022 01:43:49 -0800 Subject: [PATCH 2/8] Check const Drop impls considering ConstIfConst bounds --- .../src/transform/check_consts/qualifs.rs | 43 ++-- compiler/rustc_middle/src/traits/mod.rs | 22 +- compiler/rustc_middle/src/traits/select.rs | 4 +- .../src/traits/structural_impls.rs | 7 +- .../src/traits/select/candidate_assembly.rs | 192 ++++++------------ .../src/traits/select/confirmation.rs | 106 +++++++++- .../src/traits/select/mod.rs | 8 +- 7 files changed, 221 insertions(+), 161 deletions(-) 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 cf939aaa73f64..1269f2207d5ad 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -4,11 +4,12 @@ use rustc_errors::ErrorReported; use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::TraitEngine; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::{ - self, ImplSource, Obligation, ObligationCause, SelectionContext, + self, FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, }; use super::ConstCx; @@ -161,28 +162,46 @@ impl Qualif for NeedsNonConstDrop { // without having the lang item present. return false; }; - let trait_ref = - ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) }; + let obligation = Obligation::new( ObligationCause::dummy(), cx.param_env, ty::Binder::dummy(ty::TraitPredicate { - trait_ref, + trait_ref: ty::TraitRef { + def_id: drop_trait, + substs: cx.tcx.mk_substs_trait(ty, &[]), + }, constness: ty::BoundConstness::ConstIfConst, polarity: ty::ImplPolarity::Positive, }), ); - let implsrc = cx.tcx.infer_ctxt().enter(|infcx| { + cx.tcx.infer_ctxt().enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); - selcx.select(&obligation) - }); - !matches!( - implsrc, - Ok(Some( + let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { + // If we couldn't select a const drop candidate, then it's bad + return true; + }; + + if !matches!( + impl_src, ImplSource::ConstDrop(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) - )) - ) + ) { + // If our const drop candidate is not ConstDrop or implied by param, + // then it's bad + return true; + } + + // If we successfully found one, then select all of the predicates + // implied by our const drop impl. + let mut fcx = FulfillmentContext::new(); + for nested in impl_src.nested_obligations() { + fcx.register_predicate_obligation(&infcx, nested); + } + + // If we had any errors, then it's bad + !fcx.select_all_or_error(&infcx).is_empty() + }) } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index de5beffb5c541..b7ccfac8063d7 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -566,7 +566,7 @@ pub enum ImplSource<'tcx, N> { TraitAlias(ImplSourceTraitAliasData<'tcx, N>), /// ImplSource for a `const Drop` implementation. - ConstDrop(ImplSourceConstDropData), + ConstDrop(ImplSourceConstDropData), } impl<'tcx, N> ImplSource<'tcx, N> { @@ -581,10 +581,10 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => d.nested, ImplSource::FnPointer(d) => d.nested, ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) - | ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(), + | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), ImplSource::TraitAlias(d) => d.nested, ImplSource::TraitUpcasting(d) => d.nested, + ImplSource::ConstDrop(i) => i.nested, } } @@ -599,10 +599,10 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => &d.nested, ImplSource::FnPointer(d) => &d.nested, ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) - | ImplSource::ConstDrop(ImplSourceConstDropData) => &[], + | ImplSource::Pointee(ImplSourcePointeeData) => &[], ImplSource::TraitAlias(d) => &d.nested, ImplSource::TraitUpcasting(d) => &d.nested, + ImplSource::ConstDrop(i) => &i.nested, } } @@ -661,9 +661,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { nested: d.nested.into_iter().map(f).collect(), }) } - ImplSource::ConstDrop(ImplSourceConstDropData) => { - ImplSource::ConstDrop(ImplSourceConstDropData) - } + ImplSource::ConstDrop(i) => ImplSource::ConstDrop(ImplSourceConstDropData { + nested: i.nested.into_iter().map(f).collect(), + }), } } } @@ -755,8 +755,10 @@ pub struct ImplSourceDiscriminantKindData; #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub struct ImplSourcePointeeData; -#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] -pub struct ImplSourceConstDropData; +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceConstDropData { + pub nested: Vec, +} #[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] pub struct ImplSourceTraitAliasData<'tcx, N> { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 71ee00c602a3d..e18f04d92eec4 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -146,8 +146,8 @@ pub enum SelectionCandidate<'tcx> { BuiltinUnsizeCandidate, - /// Implementation of `const Drop`. - ConstDropCandidate, + /// Implementation of `const Drop`, optionally from a custom `impl const Drop`. + ConstDropCandidate(Option), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index aa2f37bd81ad8..6ce9f5eea3491 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -120,6 +120,12 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx, } } +impl fmt::Debug for traits::ImplSourceConstDropData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ImplSourceConstDropData(nested={:?})", self.nested) + } +} + /////////////////////////////////////////////////////////////////////////// // Lift implementations @@ -127,5 +133,4 @@ TrivialTypeFoldableAndLiftImpls! { super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, - super::ImplSourceConstDropData, } 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 b573c4b43906c..20297518a5945 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -307,13 +307,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if lang_items.drop_trait() == Some(def_id) && obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst { - if obligation.param_env.constness() == hir::Constness::Const { - self.assemble_const_drop_candidates(obligation, stack, &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) - } + self.assemble_const_drop_candidates(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -918,139 +912,77 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn assemble_const_drop_candidates<'a>( + fn assemble_const_drop_candidates( &mut self, obligation: &TraitObligation<'tcx>, - obligation_stack: &TraitObligationStack<'a, 'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'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 new_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - let mut copy_obligation = - obligation.with(obligation.predicate.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: self.tcx().require_lang_item(hir::LangItem::Copy, None), - substs: self.tcx().mk_substs_trait(ty, &[]), - }, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - })); - copy_obligation.recursion_depth = depth + 1; - self.assemble_candidates_from_impls(©_obligation, &mut new_candidates); - let copy_conditions = self.copy_clone_conditions(©_obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut new_candidates); - let copy_stack = self.push_stack(obligation_stack.list(), ©_obligation); - self.assemble_candidates_from_caller_bounds(©_stack, &mut new_candidates)?; - - let const_drop_obligation = - obligation.with(obligation.predicate.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: self.tcx().require_lang_item(hir::LangItem::Drop, None), - substs: self.tcx().mk_substs_trait(ty, &[]), - }, - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - })); - - let const_drop_stack = self.push_stack(obligation_stack.list(), &const_drop_obligation); - self.assemble_candidates_from_caller_bounds(&const_drop_stack, &mut new_candidates)?; - - if !new_candidates.vec.is_empty() { - noreturn = true; - } - debug!(?new_candidates.vec, "assemble_const_drop_candidates"); - - match ty.kind() { - ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::FnPtr(_) - | ty::Never - | ty::Ref(..) - | ty::FnDef(..) - | ty::RawPtr(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Foreign(_) => {} // Do nothing. These types satisfy `const Drop`. - - ty::Adt(def, subst) => { - let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - 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"); - } - } - - ty::Array(ty, _) => stack.push((ty, depth + 1)), - - ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))), + ) { + // If the predicate is `~const Drop` in a non-const environment, we don't actually need + // to check anything. We'll short-circuit checking any obligations in confirmation, too. + if obligation.param_env.constness() == hir::Constness::NotConst { + candidates.vec.push(ConstDropCandidate(None)); + return; + } - ty::Closure(_, substs) => { - let substs = substs.as_closure(); - let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty()); - stack.push((ty, depth + 1)); - } + let self_ty = self.infcx().shallow_resolve(obligation.self_ty()); + match self_ty.skip_binder().kind() { + ty::Opaque(..) + | ty::Dynamic(..) + | ty::Error(_) + | ty::Bound(..) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Never + | ty::Foreign(_) + | ty::Projection(_) => { + // We don't know if these are `~const Drop`, at least + // not structurally... so don't push a candidate. + } - ty::Generator(_, substs, _) => { - let substs = substs.as_generator(); - let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty()); + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(_) + | ty::Closure(..) + | ty::Generator(..) + | ty::Tuple(_) + | ty::GeneratorWitness(_) => { + // These are built-in, and cannot have a custom `impl const Drop`. + candidates.vec.push(ConstDropCandidate(None)); + } - stack.push((ty, depth + 1)); - stack.push((substs.witness(), depth + 1)); - } + ty::Adt(..) => { + // Find a custom `impl Drop` impl, if it exists + let relevant_impl = self.tcx().find_map_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + Some, + ); - ty::GeneratorWitness(tys) => stack.extend( - self.tcx().erase_late_bound_regions(*tys).iter().map(|t| (t, depth + 1)), - ), - - ty::Slice(ty) => stack.push((ty, depth + 1)), - - ty::Opaque(..) - | ty::Dynamic(..) - | ty::Error(_) - | ty::Bound(..) - | ty::Infer(_) - | ty::Placeholder(_) - | ty::Projection(..) - | ty::Param(..) => { - if !noreturn { - return Ok(()); + if let Some(impl_def_id) = relevant_impl { + // Check that `impl Drop` is actually const, if there is a custom impl + if self.tcx().impl_constness(impl_def_id) == hir::Constness::Const { + candidates.vec.push(ConstDropCandidate(Some(impl_def_id))); } - debug!("not returning"); + } else { + // Otherwise check the ADT like a built-in type (structurally) + candidates.vec.push(ConstDropCandidate(None)); } } - debug!(?stack, "assemble_const_drop_candidates - in loop"); - } - // all types have passed. - candidates.vec.push(ConstDropCandidate); - Ok(()) + ty::Infer(_) => { + candidates.ambiguous = true; + } + } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 669b6023397ee..9d62614031c22 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -80,7 +80,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { param_env: obligation.param_env.without_const(), ..*obligation }; - obligation = &new_obligation; } @@ -159,7 +158,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::TraitUpcasting(data)) } - ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)), + ConstDropCandidate(def_id) => { + let data = self.confirm_const_drop_candidate(obligation, def_id)?; + Ok(ImplSource::ConstDrop(data)) + } } } @@ -1087,4 +1089,104 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSourceBuiltinData { nested }) } + + fn confirm_const_drop_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + impl_def_id: Option, + ) -> Result>, SelectionError<'tcx>> { + // `~const Drop` in a non-const environment is always trivially true, since our type is `Drop` + if obligation.param_env.constness() == Constness::NotConst { + return Ok(ImplSourceConstDropData { nested: vec![] }); + } + + let tcx = self.tcx(); + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + + let nested_tys = match *self_ty.skip_binder().kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Projection(_) => vec![], + + ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(), + + ty::Array(ty, _) | ty::Slice(ty) => vec![ty], + + ty::Tuple(tys) => tys.iter().map(|ty| ty.expect_ty()).collect(), + + ty::Closure(_, substs) => vec![substs.as_closure().tupled_upvars_ty()], + + ty::Generator(_, substs, _) => { + vec![substs.as_generator().tupled_upvars_ty(), substs.as_generator().witness()] + } + + ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys).to_vec(), + + ty::Opaque(_, _) + | ty::Dynamic(_, _) + | ty::Error(_) + | ty::Bound(_, _) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Never + | ty::Foreign(_) + | ty::Infer(_) => { + unreachable!(); + } + }; + + info!( + "confirm_const_drop_candidate: self_ty={:?}, nested_tys={:?} impl_def_id={:?}", + self_ty, nested_tys, impl_def_id + ); + + let cause = obligation.derived_cause(BuiltinDerivedObligation); + let mut nested = vec![]; + + // If we have a custom `impl const Drop`, then check it like a regular impl candidate. + if let Some(impl_def_id) = impl_def_id { + nested.extend(self.confirm_impl_candidate(obligation, impl_def_id).nested); + } + + for ty in nested_tys { + let predicate = self.infcx.commit_unconditionally(|_| { + normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + self_ty + .rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: self.tcx().require_lang_item(LangItem::Drop, None), + substs: self.tcx().mk_substs_trait(ty, &[]), + }, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }) + .to_predicate(tcx), + &mut nested, + ) + }); + + nested.push(Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + } + + Ok(ImplSourceConstDropData { nested }) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1414c742635c4..558ecd393b614 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1153,7 +1153,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { GeneratorCandidate => {} // FnDef where the function is const FnPointerCandidate { is_const: true } => {} - ConstDropCandidate => {} + ConstDropCandidate(_) => {} _ => { // reject all other types of candidates continue; @@ -1537,7 +1537,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, - // and `DiscriminantKindCandidate` to anything else. + // `DiscriminantKindCandidate`, and `ConstDropCandidate` to anything else. // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. @@ -1554,7 +1554,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate | PointeeCandidate - | ConstDropCandidate, + | ConstDropCandidate(_), _, ) => true, ( @@ -1562,7 +1562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate | PointeeCandidate - | ConstDropCandidate, + | ConstDropCandidate(_), ) => false, (ParamCandidate(other), ParamCandidate(victim)) => { From 33e5efbd586cbdc683cb54949fe163755c57e9e8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 18 Jan 2022 01:42:35 -0800 Subject: [PATCH 3/8] adjust tests --- .../const-drop-fail.precise.stderr | 57 ++++++++----------- .../const-drop-fail.rs | 4 +- .../const-drop-fail.stock.stderr | 57 ++++++++----------- .../rfc-2632-const-trait-impl/const-drop.rs | 16 ++++++ 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr index 04c21101e758b..721636e074371 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr @@ -1,13 +1,5 @@ -error: `~const` is not allowed here - --> $DIR/const-drop-fail.rs:27:35 - | -LL | struct ConstDropImplWithBounds(PhantomData); - | ^^^^^^^^ - | - = note: only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions - error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:45:5 + --> $DIR/const-drop-fail.rs:44:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -16,50 +8,51 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:36:19 + --> $DIR/const-drop-fail.rs:35: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:47:5 +error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied in `ConstImplWithDropGlue` + --> $DIR/const-drop-fail.rs:46:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call ... LL | ConstImplWithDropGlue(NonTrivialDrop), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `ConstImplWithDropGlue`, the trait `Drop` is not implemented for `NonTrivialDrop` | +note: required because it appears within the type `ConstImplWithDropGlue` + --> $DIR/const-drop-fail.rs:17:8 + | +LL | struct ConstImplWithDropGlue(NonTrivialDrop); + | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:36:19 + --> $DIR/const-drop-fail.rs:35:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied - --> $DIR/const-drop-fail.rs:49:5 - | -LL | ConstDropImplWithBounds::(PhantomData), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` - | -note: required by a bound in `ConstDropImplWithBounds` - --> $DIR/const-drop-fail.rs:27:35 - | -LL | struct ConstDropImplWithBounds(PhantomData); - | ^^^^^^^^ required by this bound in `ConstDropImplWithBounds` - -error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied - --> $DIR/const-drop-fail.rs:49:5 + --> $DIR/const-drop-fail.rs:48:5 | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... LL | ConstDropImplWithBounds::(PhantomData), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` | -note: required by a bound in `ConstDropImplWithBounds` - --> $DIR/const-drop-fail.rs:27:35 +note: required because of the requirements on the impl of `Drop` for `ConstDropImplWithBounds` + --> $DIR/const-drop-fail.rs:29:25 | -LL | struct ConstDropImplWithBounds(PhantomData); - | ^^^^^^^^ required by this bound in `ConstDropImplWithBounds` +LL | impl const Drop for ConstDropImplWithBounds { + | ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:35:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. 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 3d4de088f5530..4622723c1891e 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 @@ -24,8 +24,7 @@ trait A { fn a() { println!("A"); } } impl A for NonTrivialDrop {} -struct ConstDropImplWithBounds(PhantomData); -//~^ ERROR `~const` is not allowed +struct ConstDropImplWithBounds(PhantomData); impl const Drop for ConstDropImplWithBounds { fn drop(&mut self) { @@ -48,7 +47,6 @@ check_all! { //~^ ERROR the trait bound ConstDropImplWithBounds::(PhantomData), //~^ ERROR the trait bound - //~| ERROR the trait bound } fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr index 04c21101e758b..721636e074371 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr @@ -1,13 +1,5 @@ -error: `~const` is not allowed here - --> $DIR/const-drop-fail.rs:27:35 - | -LL | struct ConstDropImplWithBounds(PhantomData); - | ^^^^^^^^ - | - = note: only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions - error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:45:5 + --> $DIR/const-drop-fail.rs:44:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -16,50 +8,51 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:36:19 + --> $DIR/const-drop-fail.rs:35: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:47:5 +error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied in `ConstImplWithDropGlue` + --> $DIR/const-drop-fail.rs:46:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call ... LL | ConstImplWithDropGlue(NonTrivialDrop), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `ConstImplWithDropGlue`, the trait `Drop` is not implemented for `NonTrivialDrop` | +note: required because it appears within the type `ConstImplWithDropGlue` + --> $DIR/const-drop-fail.rs:17:8 + | +LL | struct ConstImplWithDropGlue(NonTrivialDrop); + | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:36:19 + --> $DIR/const-drop-fail.rs:35:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied - --> $DIR/const-drop-fail.rs:49:5 - | -LL | ConstDropImplWithBounds::(PhantomData), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` - | -note: required by a bound in `ConstDropImplWithBounds` - --> $DIR/const-drop-fail.rs:27:35 - | -LL | struct ConstDropImplWithBounds(PhantomData); - | ^^^^^^^^ required by this bound in `ConstDropImplWithBounds` - -error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied - --> $DIR/const-drop-fail.rs:49:5 + --> $DIR/const-drop-fail.rs:48:5 | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... LL | ConstDropImplWithBounds::(PhantomData), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` | -note: required by a bound in `ConstDropImplWithBounds` - --> $DIR/const-drop-fail.rs:27:35 +note: required because of the requirements on the impl of `Drop` for `ConstDropImplWithBounds` + --> $DIR/const-drop-fail.rs:29:25 | -LL | struct ConstDropImplWithBounds(PhantomData); - | ^^^^^^^^ required by this bound in `ConstDropImplWithBounds` +LL | impl const Drop for ConstDropImplWithBounds { + | ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:35:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs index 292017a1de24d..d244f75ed36e2 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -45,6 +45,21 @@ mod t { pub struct HasConstDrop(pub ConstDrop); pub struct TrivialFields(pub u8, pub i8, pub usize, pub isize); + + pub trait SomeTrait { + fn foo(); + } + impl const SomeTrait for () { + fn foo() {} + } + + pub struct ConstDropWithBound(pub core::marker::PhantomData); + + impl const Drop for ConstDropWithBound { + fn drop(&mut self) { + T::foo(); + } + } } use t::*; @@ -61,6 +76,7 @@ implements_const_drop! { TrivialFields(1, 2, 3, 4), &1, &1 as *const i32, + ConstDropWithBound::<()>, } fn main() { From ba87be05cfa8f9a02d673a64aa2ee83976d80e60 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 18 Jan 2022 17:01:52 -0800 Subject: [PATCH 4/8] Short-circuit some trivially const Drop types --- .../src/transform/check_consts/qualifs.rs | 19 +++++----- compiler/rustc_middle/src/ty/util.rs | 36 +++++++++++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) 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 1269f2207d5ad..8dfdbf5d9dda2 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -146,15 +146,10 @@ impl Qualif for NeedsNonConstDrop { qualifs.needs_non_const_drop } - fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, mut ty: Ty<'tcx>) -> bool { - // Avoid selecting for simple cases. - match ty::util::needs_drop_components(ty, &cx.tcx.data_layout).as_deref() { - Ok([]) => return false, - Err(ty::util::AlwaysRequiresDrop) => return true, - // If we've got a single component, select with that - // to increase the chance that we hit the selection cache. - Ok([t]) => ty = t, - Ok([..]) => {} + fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { + // Avoid selecting for simple cases, such as builtin types. + if ty::util::trivial_const_drop(ty) { + return false; } let Some(drop_trait) = cx.tcx.lang_items().drop_trait() else { @@ -187,11 +182,15 @@ impl Qualif for NeedsNonConstDrop { impl_src, ImplSource::ConstDrop(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) ) { - // If our const drop candidate is not ConstDrop or implied by param, + // If our const drop candidate is not ConstDrop or implied by the param env, // then it's bad return true; } + if impl_src.borrow_nested_obligations().is_empty() { + return false; + } + // If we successfully found one, then select all of the predicates // implied by our const drop impl. let mut fcx = FulfillmentContext::new(); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 8793264a47fbb..71e9197e4f59f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1041,6 +1041,42 @@ pub fn needs_drop_components<'tcx>( } } +pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { + match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) => true, + + ty::Opaque(..) + | ty::Dynamic(..) + | ty::Error(_) + | ty::Bound(..) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Never + | ty::Foreign(_) + | ty::Projection(_) + | ty::Infer(_) => false, + + // Not trivial because they have components, and instead of looking inside, + // we'll just perform trait selection. + ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(_) | ty::Adt(..) => false, + + ty::Array(ty, _) | ty::Slice(ty) => trivial_const_drop(ty), + + ty::Tuple(tys) => tys.iter().all(|ty| trivial_const_drop(ty.expect_ty())), + } +} + // Does the equivalent of // ``` // let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); From e3f01b2b6ff177359504e42a99665a8abc388cb7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jan 2022 00:40:05 -0800 Subject: [PATCH 5/8] never type is const Drop --- .../src/traits/select/candidate_assembly.rs | 2 +- .../rustc_trait_selection/src/traits/select/confirmation.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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 20297518a5945..77db3bd2692cc 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -932,7 +932,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Bound(..) | ty::Param(_) | ty::Placeholder(_) - | ty::Never | ty::Foreign(_) | ty::Projection(_) => { // We don't know if these are `~const Drop`, at least @@ -951,6 +950,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) + | ty::Never | ty::Array(..) | ty::Slice(_) | ty::Closure(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 9d62614031c22..35aebd9900fc9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1103,6 +1103,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + // Skip binder here (*) let nested_tys = match *self_ty.skip_binder().kind() { ty::Bool | ty::Char @@ -1116,7 +1117,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) - | ty::Projection(_) => vec![], + | ty::Never => vec![], ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(), @@ -1138,8 +1139,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Bound(_, _) | ty::Param(_) | ty::Placeholder(_) - | ty::Never | ty::Foreign(_) + | ty::Projection(_) | ty::Infer(_) => { unreachable!(); } @@ -1165,6 +1166,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, cause.clone(), obligation.recursion_depth + 1, + // Rebinding here (*) self_ty .rebind(ty::TraitPredicate { trait_ref: ty::TraitRef { From 0eccd5feef7ff3dc9d903368ae39acb54179a424 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jan 2022 01:23:23 -0800 Subject: [PATCH 6/8] skip some layers in const drop confirmation --- .../src/traits/select/confirmation.rs | 161 ++++++++++-------- 1 file changed, 91 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 35aebd9900fc9..4c74627f3c67d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1103,90 +1103,111 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); - // Skip binder here (*) - let nested_tys = match *self_ty.skip_binder().kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Never => vec![], - - ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(), - - ty::Array(ty, _) | ty::Slice(ty) => vec![ty], - - ty::Tuple(tys) => tys.iter().map(|ty| ty.expect_ty()).collect(), - - ty::Closure(_, substs) => vec![substs.as_closure().tupled_upvars_ty()], - - ty::Generator(_, substs, _) => { - vec![substs.as_generator().tupled_upvars_ty(), substs.as_generator().witness()] - } - - ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys).to_vec(), - - ty::Opaque(_, _) - | ty::Dynamic(_, _) - | ty::Error(_) - | ty::Bound(_, _) - | ty::Param(_) - | ty::Placeholder(_) - | ty::Foreign(_) - | ty::Projection(_) - | ty::Infer(_) => { - unreachable!(); - } - }; - - info!( - "confirm_const_drop_candidate: self_ty={:?}, nested_tys={:?} impl_def_id={:?}", - self_ty, nested_tys, impl_def_id - ); - - let cause = obligation.derived_cause(BuiltinDerivedObligation); let mut nested = vec![]; + let cause = obligation.derived_cause(BuiltinDerivedObligation); - // If we have a custom `impl const Drop`, then check it like a regular impl candidate. + // If we have a custom `impl const Drop`, then + // first check it like a regular impl candidate if let Some(impl_def_id) = impl_def_id { nested.extend(self.confirm_impl_candidate(obligation, impl_def_id).nested); } - for ty in nested_tys { - let predicate = self.infcx.commit_unconditionally(|_| { - normalize_with_depth_to( - self, - obligation.param_env, - cause.clone(), - obligation.recursion_depth + 1, - // Rebinding here (*) - self_ty + // We want to confirm the ADT's fields if we have an ADT + let mut stack = match *self_ty.skip_binder().kind() { + ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(), + _ => vec![self_ty.skip_binder()], + }; + + while let Some(nested_ty) = stack.pop() { + match *nested_ty.kind() { + // We know these types are trivially drop + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Never => {} + + // These types are built-in, so we can fast-track by registering + // nested predicates for their constituient type(s) + ty::Array(ty, _) | ty::Slice(ty) => { + stack.push(ty); + } + ty::Tuple(tys) => { + stack.extend(tys.iter().map(|ty| ty.expect_ty())); + } + ty::Closure(_, substs) => { + stack.push(substs.as_closure().tupled_upvars_ty()); + } + ty::Generator(_, substs, _) => { + let generator = substs.as_generator(); + stack.extend([generator.tupled_upvars_ty(), generator.witness()]); + } + ty::GeneratorWitness(tys) => { + stack.extend(tcx.erase_late_bound_regions(tys).to_vec()); + } + + // If we have a projection type, make sure to normalize it so we replace it + // with a fresh infer variable + ty::Projection(..) => { + self.infcx.commit_unconditionally(|_| { + let predicate = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + self_ty + .rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: self.tcx().require_lang_item(LangItem::Drop, None), + substs: self.tcx().mk_substs_trait(nested_ty, &[]), + }, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }) + .to_predicate(tcx), + &mut nested, + ); + + nested.push(Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + }); + } + + // If we have any other type (e.g. an ADT), just register a nested obligation + // since it's either not `const Drop` (and we raise an error during selection), + // or it's an ADT (and we need to check for a custom impl during selection) + _ => { + let predicate = self_ty .rebind(ty::TraitPredicate { trait_ref: ty::TraitRef { def_id: self.tcx().require_lang_item(LangItem::Drop, None), - substs: self.tcx().mk_substs_trait(ty, &[]), + substs: self.tcx().mk_substs_trait(nested_ty, &[]), }, constness: ty::BoundConstness::ConstIfConst, polarity: ty::ImplPolarity::Positive, }) - .to_predicate(tcx), - &mut nested, - ) - }); + .to_predicate(tcx); - nested.push(Obligation::with_depth( - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); + nested.push(Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + } + } } Ok(ImplSourceConstDropData { nested }) From 8547f5732c003080f940a23eaf3e2e9c052934ae Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jan 2022 12:59:28 -0800 Subject: [PATCH 7/8] never is trivially const-drop, and add test --- compiler/rustc_middle/src/ty/util.rs | 4 ++-- src/test/ui/rfc-2632-const-trait-impl/const-drop.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 71e9197e4f59f..d73fcc28c13b0 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1054,7 +1054,8 @@ pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { | ty::RawPtr(_) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) => true, + | ty::FnPtr(_) + | ty::Never => true, ty::Opaque(..) | ty::Dynamic(..) @@ -1062,7 +1063,6 @@ pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { | ty::Bound(..) | ty::Param(_) | ty::Placeholder(_) - | ty::Never | ty::Foreign(_) | ty::Projection(_) | ty::Infer(_) => false, diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs index d244f75ed36e2..7d610e5277d11 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -3,6 +3,7 @@ #![feature(const_trait_impl)] #![feature(const_fn_trait_bound)] #![feature(const_mut_refs)] +#![feature(never_type)] #![cfg_attr(precise, feature(const_precise_live_drops))] struct S<'a>(&'a mut u8); @@ -77,6 +78,7 @@ implements_const_drop! { &1, &1 as *const i32, ConstDropWithBound::<()>, + Result::::Ok(1), } fn main() { From b7e443397471062e5681ecefb6638577cefb9571 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jan 2022 20:07:04 -0800 Subject: [PATCH 8/8] Foreign types are trivially drop - Also rename a trivial_const_drop to match style of other functions in the util module. - Also add a test for `const Drop` that doesn't depend on a `~const` bound. - Also comment a bit why we remove the const bound during dropck impl check. --- .../src/transform/check_consts/qualifs.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 10 +++++----- .../src/traits/select/candidate_assembly.rs | 2 +- .../src/traits/select/confirmation.rs | 3 ++- compiler/rustc_typeck/src/check/dropck.rs | 18 +++++++++--------- .../ui/rfc-2632-const-trait-impl/const-drop.rs | 13 +++++++++++++ 6 files changed, 31 insertions(+), 17 deletions(-) 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 8dfdbf5d9dda2..91610b15eb999 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -148,7 +148,7 @@ impl Qualif for NeedsNonConstDrop { fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { // Avoid selecting for simple cases, such as builtin types. - if ty::util::trivial_const_drop(ty) { + if ty::util::is_trivially_const_drop(ty) { return false; } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index d73fcc28c13b0..96c27d649e4d1 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1041,7 +1041,7 @@ pub fn needs_drop_components<'tcx>( } } -pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { +pub fn is_trivially_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { match *ty.kind() { ty::Bool | ty::Char @@ -1055,7 +1055,8 @@ pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) - | ty::Never => true, + | ty::Never + | ty::Foreign(_) => true, ty::Opaque(..) | ty::Dynamic(..) @@ -1063,7 +1064,6 @@ pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { | ty::Bound(..) | ty::Param(_) | ty::Placeholder(_) - | ty::Foreign(_) | ty::Projection(_) | ty::Infer(_) => false, @@ -1071,9 +1071,9 @@ pub fn trivial_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { // we'll just perform trait selection. ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(_) | ty::Adt(..) => false, - ty::Array(ty, _) | ty::Slice(ty) => trivial_const_drop(ty), + ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty), - ty::Tuple(tys) => tys.iter().all(|ty| trivial_const_drop(ty.expect_ty())), + ty::Tuple(tys) => tys.iter().all(|ty| is_trivially_const_drop(ty.expect_ty())), } } 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 77db3bd2692cc..0099fba920042 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -932,7 +932,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Bound(..) | ty::Param(_) | ty::Placeholder(_) - | ty::Foreign(_) | ty::Projection(_) => { // We don't know if these are `~const Drop`, at least // not structurally... so don't push a candidate. @@ -951,6 +950,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::FnDef(..) | ty::FnPtr(_) | ty::Never + | ty::Foreign(_) | ty::Array(..) | ty::Slice(_) | ty::Closure(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4c74627f3c67d..3b6a4afafcfd1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1133,7 +1133,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) - | ty::Never => {} + | ty::Never + | ty::Foreign(_) => {} // These types are built-in, so we can fast-track by registering // nested predicates for their constituient type(s) diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 46610a0e08db6..89866c20b61d6 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -228,15 +228,15 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let predicate = predicate.kind(); let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { - (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => relator - .relate( - predicate.rebind(ty::TraitPredicate { - constness: ty::BoundConstness::NotConst, - ..a - }), - p.rebind(b), - ) - .is_ok(), + (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { + // Since struct predicates cannot have ~const, project the impl predicate + // onto one that ignores the constness. This is equivalent to saying that + // we match a `Trait` bound on the struct with a `Trait` or `~const Trait` + // in the impl. + let non_const_a = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }; + relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok() + } (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs index 7d610e5277d11..13363c506d54e 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -53,6 +53,10 @@ mod t { impl const SomeTrait for () { fn foo() {} } + // non-const impl + impl SomeTrait for i32 { + fn foo() {} + } pub struct ConstDropWithBound(pub core::marker::PhantomData); @@ -61,6 +65,14 @@ mod t { T::foo(); } } + + pub struct ConstDropWithNonconstBound(pub core::marker::PhantomData); + + impl const Drop for ConstDropWithNonconstBound { + fn drop(&mut self) { + // Note: we DON'T use the `T: SomeTrait` bound + } + } } use t::*; @@ -78,6 +90,7 @@ implements_const_drop! { &1, &1 as *const i32, ConstDropWithBound::<()>, + ConstDropWithNonconstBound::, Result::::Ok(1), }