From a13b13ff46b2d669f0d7a8e024938366fb4a3281 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 16:34:28 +0000 Subject: [PATCH] Const drop selection candidates --- compiler/rustc_middle/src/traits/mod.rs | 15 ++- compiler/rustc_middle/src/traits/select.rs | 3 + .../src/traits/structural_impls.rs | 3 + .../src/traits/project.rs | 6 +- .../src/traits/select/candidate_assembly.rs | 110 +++++++++++++++++- .../src/traits/select/confirmation.rs | 4 +- .../src/traits/select/mod.rs | 42 ++++--- compiler/rustc_ty_utils/src/instance.rs | 3 +- .../rfc-2632-const-trait-impl/const-drop.rs | 51 ++++++++ 9 files changed, 212 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 74edb17fe32f..444bc2ff043d 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -529,6 +529,9 @@ pub enum ImplSource<'tcx, N> { /// ImplSource for a trait alias. TraitAlias(ImplSourceTraitAliasData<'tcx, N>), + + /// ImplSource for a `const Drop` implementation. + ConstDrop(ImplSourceConstDropData), } impl<'tcx, N> ImplSource<'tcx, N> { @@ -543,7 +546,8 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => d.nested, ImplSource::FnPointer(d) => d.nested, ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), + | ImplSource::Pointee(ImplSourcePointeeData) + | ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(), ImplSource::TraitAlias(d) => d.nested, ImplSource::TraitUpcasting(d) => d.nested, } @@ -560,7 +564,8 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => &d.nested[..], ImplSource::FnPointer(d) => &d.nested[..], ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) => &[], + | ImplSource::Pointee(ImplSourcePointeeData) + | ImplSource::ConstDrop(ImplSourceConstDropData) => &[], ImplSource::TraitAlias(d) => &d.nested[..], ImplSource::TraitUpcasting(d) => &d.nested[..], } @@ -621,6 +626,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { nested: d.nested.into_iter().map(f).collect(), }) } + ImplSource::ConstDrop(ImplSourceConstDropData) => { + ImplSource::ConstDrop(ImplSourceConstDropData) + } } } } @@ -712,6 +720,9 @@ 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 ImplSourceTraitAliasData<'tcx, N> { pub alias_def_id: DefId, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 62996bf4cbef..3c0fedb36082 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -143,6 +143,9 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, + + /// Implementation of `const Drop`. + ConstDropCandidate, } /// 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 aa16e14fedcd..d0a2d0d9afb3 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -32,6 +32,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d), super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), + + super::ImplSource::ConstDrop(ref d) => write!(f, "{:?}", d), } } } @@ -125,4 +127,5 @@ TrivialTypeFoldableAndLiftImpls! { super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, + super::ImplSourceConstDropData, } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 7038f16a2c9c..491092476635 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1477,7 +1477,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) - | super::ImplSource::TraitUpcasting(_) => { + | super::ImplSource::TraitUpcasting(_) + | super::ImplSource::ConstDrop(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1549,7 +1550,8 @@ fn confirm_select_candidate<'cx, 'tcx>( | super::ImplSource::Param(..) | super::ImplSource::Builtin(..) | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TraitAlias(..) => { + | super::ImplSource::TraitAlias(..) + | super::ImplSource::ConstDrop(_) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, 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 e18828fec3f6..ffaef566cd59 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; use rustc_target::spec::abi::Abi; use crate::traits::coherence::Conflict; @@ -277,6 +277,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else if lang_items.drop_trait() == Some(def_id) + && obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst + { + if self.is_in_const_context { + self.assemble_const_drop_candidates(obligation, &mut candidates)?; + } else { + // `~const Drop` when we are not in a const context has no effect. + candidates.vec.push(ConstDropCandidate) + } } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -803,4 +812,103 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_const_drop_candidates( + &mut self, + obligation: &TraitObligation<'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() { + self.check_recursion_depth(depth, obligation)?; + let mut copy_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, + })); + copy_obligation.recursion_depth = depth + 1; + self.assemble_candidates_from_impls(©_obligation, &mut copy_candidates); + 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; + } + + 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, &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))); + } + } + + ty::Array(ty, _) => stack.push((ty, depth + 1)), + + ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))), + + ty::Closure(_, substs) => { + stack.extend(substs.as_closure().upvar_tys().map(|t| (t, depth + 1))) + } + + ty::Generator(_, substs, _) => { + let substs = substs.as_generator(); + stack.extend(substs.upvar_tys().map(|t| (t, depth + 1))); + stack.push((substs.witness(), depth + 1)); + } + + 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(..) => return Ok(()), + } + } + // all types have passed. + candidates.vec.push(ConstDropCandidate); + + Ok(()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 6fae8173985b..3b6555de912e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,7 +28,7 @@ use crate::traits::TraitNotObjectSafe; use crate::traits::VtblSegment; use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation}; use crate::traits::{ - ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, + ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, ImplSourceConstDropData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, @@ -124,6 +124,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; Ok(ImplSource::TraitUpcasting(data)) } + + ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)), } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 22013fb79cf7..777e4ffc080a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1038,6 +1038,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); } + fn check_recursion_depth>( + &self, + depth: usize, + error_obligation: &Obligation<'tcx, T>, + ) -> Result<(), OverflowError> { + if !self.infcx.tcx.recursion_limit().value_within_limit(depth) { + match self.query_mode { + TraitQueryMode::Standard => { + self.infcx.report_overflow_error(error_obligation, true); + } + TraitQueryMode::Canonical => { + return Err(OverflowError); + } + } + } + Ok(()) + } + /// Checks that the recursion limit has not been exceeded. /// /// The weird return type of this function allows it to be used with the `try` (`?`) @@ -1047,17 +1065,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &Obligation<'tcx, T>, error_obligation: &Obligation<'tcx, V>, ) -> Result<(), OverflowError> { - if !self.infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { - match self.query_mode { - TraitQueryMode::Standard => { - self.infcx().report_overflow_error(error_obligation, true); - } - TraitQueryMode::Canonical => { - return Err(OverflowError); - } - } - } - Ok(()) + self.check_recursion_depth(obligation.recursion_depth, error_obligation) } fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) @@ -1079,10 +1087,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); // Respect const trait obligations if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { - if Some(obligation.predicate.skip_binder().trait_ref.def_id) - != tcx.lang_items().sized_trait() - // const Sized bounds are skipped - { match candidate { // const impl ImplCandidate(def_id) @@ -1097,12 +1101,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // generator, this will raise error in other places // or ignore error with const_async_blocks feature GeneratorCandidate => {} + ConstDropCandidate => {} _ => { // reject all other types of candidates return Err(Unimplemented); } } - } } // Treat negative impls as unimplemented, and reservation impls as ambiguity. if let ImplCandidate(def_id) = candidate { @@ -1476,14 +1480,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ( BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate - | PointeeCandidate, + | PointeeCandidate + | ConstDropCandidate, _, ) => true, ( _, BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate - | PointeeCandidate, + | PointeeCandidate + | ConstDropCandidate, ) => false, (ParamCandidate(other), ParamCandidate(victim)) => { diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index b00d2ab35617..87b729faa54e 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -386,7 +386,8 @@ fn resolve_associated_item<'tcx>( | traits::ImplSource::TraitAlias(..) | traits::ImplSource::DiscriminantKind(..) | traits::ImplSource::Pointee(..) - | traits::ImplSource::TraitUpcasting(_) => None, + | traits::ImplSource::TraitUpcasting(_) + | traits::ImplSource::ConstDrop(_) => None, }) } 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 24b0d53b6c3e..3fcfd42f94e0 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 @@ -23,6 +23,57 @@ const fn b() -> u8 { const C: u8 = b(); +macro_rules! implements_const_drop { + ($($exp:expr),*$(,)?) => { + $( + const _: () = a($exp); + )* + } +} + +#[allow(dead_code)] +mod t { + pub struct Foo; + pub enum Bar { A } + pub fn foo() {} + pub struct ConstDrop; + + impl const Drop for ConstDrop { + fn drop(&mut self) {} + } + + pub struct HasConstDrop(pub ConstDrop); + pub struct TrivialFields(pub u8, pub i8, pub usize, pub isize); +} + +use t::*; + +implements_const_drop! { + 1u8, + 2, + 3.0, + Foo, + Bar::A, + foo, + ConstDrop, + HasConstDrop(ConstDrop), + TrivialFields(1, 2, 3, 4), + &1, + &1 as *const i32, +} + fn main() { + struct HasDropGlue(Box); + struct HasDropImpl; + impl Drop for HasDropImpl { + fn drop(&mut self) { + println!("not trivial drop"); + } + } + + // These types should pass because ~const in a non-const context should have no effect. + a(HasDropGlue(Box::new(0))); + a(HasDropImpl); + assert_eq!(C, 2); }