diff options
| author | Camille GILLOT <gillot.camille@gmail.com> | 2020-01-05 23:43:45 +0100 |
|---|---|---|
| committer | Camille GILLOT <gillot.camille@gmail.com> | 2020-01-07 18:14:32 +0100 |
| commit | f629baf96cd0dd741f4490e782a72b2ff40b27fd (patch) | |
| tree | 95b1f8c21b80f0306aa4c3d3eae6db6fb71aee1f | |
| parent | 86ec4b5f8508854321e9438902bbbcbbcd5d9a7f (diff) | |
| download | rust-f629baf96cd0dd741f4490e782a72b2ff40b27fd.tar.gz rust-f629baf96cd0dd741f4490e782a72b2ff40b27fd.zip | |
Move magic traits queries to rustc::traits::drop.
| -rw-r--r-- | src/librustc/traits/drop.rs | 202 | ||||
| -rw-r--r-- | src/librustc/traits/mod.rs | 2 | ||||
| -rw-r--r-- | src/librustc/ty/mod.rs | 1 | ||||
| -rw-r--r-- | src/librustc/ty/util.rs | 199 | ||||
| -rw-r--r-- | src/librustc_lint/builtin.rs | 3 | ||||
| -rw-r--r-- | src/librustc_passes/stability.rs | 3 | ||||
| -rw-r--r-- | src/librustc_typeck/coherence/builtin.rs | 4 |
7 files changed, 211 insertions, 203 deletions
diff --git a/src/librustc/traits/drop.rs b/src/librustc/traits/drop.rs new file mode 100644 index 00000000000..08c3a77bf3a --- /dev/null +++ b/src/librustc/traits/drop.rs @@ -0,0 +1,202 @@ +//! Miscellaneous type-system utilities that are too small to deserve their own modules. + +use crate::middle::lang_items; +use crate::traits::{self, ObligationCause}; +use crate::ty::util::NeedsDrop; +use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; + +use rustc_hir as hir; +use rustc_span::DUMMY_SP; + +#[derive(Clone)] +pub enum CopyImplementationError<'tcx> { + InfrigingFields(Vec<&'tcx ty::FieldDef>), + NotAnAdt, + HasDestructor, +} + +pub fn can_type_implement_copy( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, +) -> Result<(), CopyImplementationError<'tcx>> { + // FIXME: (@jroesch) float this code up + tcx.infer_ctxt().enter(|infcx| { + let (adt, substs) = match self_type.kind { + // These types used to have a builtin impl. + // Now libcore provides that impl. + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()), + + ty::Adt(adt, substs) => (adt, substs), + + _ => return Err(CopyImplementationError::NotAnAdt), + }; + + let mut infringing = Vec::new(); + for variant in &adt.variants { + for field in &variant.fields { + let ty = field.ty(tcx, substs); + if ty.references_error() { + continue; + } + let span = tcx.def_span(field.did); + let cause = ObligationCause { span, ..ObligationCause::dummy() }; + let ctx = traits::FulfillmentContext::new(); + match traits::fully_normalize(&infcx, ctx, cause, param_env, &ty) { + Ok(ty) => { + if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { + infringing.push(field); + } + } + Err(errors) => { + infcx.report_fulfillment_errors(&errors, None, false); + } + }; + } + } + if !infringing.is_empty() { + return Err(CopyImplementationError::InfrigingFields(infringing)); + } + if adt.has_dtor(tcx) { + return Err(CopyImplementationError::HasDestructor); + } + + Ok(()) + }) +} + +fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, lang_items::CopyTraitLangItem) +} + +fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, lang_items::SizedTraitLangItem) +} + +fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, lang_items::FreezeTraitLangItem) +} + +fn is_item_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + item: lang_items::LangItem, +) -> bool { + let (param_env, ty) = query.into_parts(); + let trait_def_id = tcx.require_lang_item(item, None); + tcx.infer_ctxt().enter(|infcx| { + traits::type_known_to_meet_bound_modulo_regions( + &infcx, + param_env, + ty, + trait_def_id, + DUMMY_SP, + ) + }) +} + +fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop { + let (param_env, ty) = query.into_parts(); + + let needs_drop = |ty: Ty<'tcx>| -> bool { tcx.needs_drop_raw(param_env.and(ty)).0 }; + + assert!(!ty.needs_infer()); + + NeedsDrop(match ty.kind { + // Fast-path for primitive types + ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Bool + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Never + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Char + | ty::GeneratorWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Str => false, + + // Foreign types can never have destructors + ty::Foreign(..) => false, + + // `ManuallyDrop` doesn't have a destructor regardless of field types. + ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false, + + // Issue #22536: We first query `is_copy_modulo_regions`. It sees a + // normalized version of the type, and therefore will definitely + // know whether the type implements Copy (and thus needs no + // cleanup/drop/zeroing) ... + _ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false, + + // ... (issue #22536 continued) but as an optimization, still use + // prior logic of asking for the structural "may drop". + + // FIXME(#22815): Note that this is a conservative heuristic; + // it may report that the type "may drop" when actual type does + // not actually have a destructor associated with it. But since + // the type absolutely did not have the `Copy` bound attached + // (see above), it is sound to treat it as having a destructor. + + // User destructors are the only way to have concrete drop types. + ty::Adt(def, _) if def.has_dtor(tcx) => true, + + // Can refer to a type which may drop. + // FIXME(eddyb) check this against a ParamEnv. + ty::Dynamic(..) + | ty::Projection(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Opaque(..) + | ty::Infer(_) + | ty::Error => true, + + ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), + + // Zero-length arrays never contain anything to drop. + ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false, + + // Structural recursion. + ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty), + + ty::Closure(def_id, ref substs) => { + substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop) + } + + // Pessimistically assume that all generators will require destructors + // as we don't know if a destructor is a noop or not until after the MIR + // state transformation pass + ty::Generator(..) => true, + + ty::Tuple(..) => ty.tuple_fields().any(needs_drop), + + // unions don't have destructors because of the child types, + // only if they manually implement `Drop` (handled above). + ty::Adt(def, _) if def.is_union() => false, + + ty::Adt(def, substs) => def + .variants + .iter() + .any(|variant| variant.fields.iter().any(|field| needs_drop(field.ty(tcx, substs)))), + }) +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + *providers = ty::query::Providers { + is_copy_raw, + is_sized_raw, + is_freeze_raw, + needs_drop_raw, + ..*providers + }; +} diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 3c49d1b6f36..11473dc2a3a 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -7,6 +7,7 @@ pub mod auto_trait; mod chalk_fulfill; pub mod codegen; mod coherence; +pub mod drop; mod engine; pub mod error_reporting; mod fulfill; @@ -1243,6 +1244,7 @@ impl<'tcx> TraitObligation<'tcx> { } pub fn provide(providers: &mut ty::query::Providers<'_>) { + drop::provide(providers); *providers = ty::query::Providers { is_object_safe: object_safety::is_object_safe_provider, specialization_graph_of: specialize::specialization_graph_provider, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 5457516a6a2..7cca12308e6 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -3318,7 +3318,6 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { context::provide(providers); erase_regions::provide(providers); layout::provide(providers); - util::provide(providers); constness::provide(providers); *providers = ty::query::Providers { asyncness, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index ee99176dc47..aa93f35661a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -2,9 +2,7 @@ use crate::hir::map::DefPathData; use crate::ich::NodeIdHashingMode; -use crate::middle::lang_items; use crate::mir::interpret::{sign_extend, truncate}; -use crate::traits::{self, ObligationCause}; use crate::ty::layout::{Integer, IntegerExt}; use crate::ty::query::TyCtxtAt; use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef}; @@ -18,7 +16,7 @@ use rustc_hir::def_id::DefId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use std::{cmp, fmt}; use syntax::ast; use syntax::attr::{self, SignedInt, UnsignedInt}; @@ -122,13 +120,6 @@ impl IntTypeExt for attr::IntType { } } -#[derive(Clone)] -pub enum CopyImplementationError<'tcx> { - InfrigingFields(Vec<&'tcx ty::FieldDef>), - NotAnAdt, - HasDestructor, -} - /// Describes whether a type is representable. For types that are not /// representable, 'SelfRecursive' and 'ContainsRecursive' are used to /// distinguish between types that are recursive with themselves and types that @@ -144,65 +135,6 @@ pub enum Representability { SelfRecursive(Vec<Span>), } -impl<'tcx> ty::ParamEnv<'tcx> { - pub fn can_type_implement_copy( - self, - tcx: TyCtxt<'tcx>, - self_type: Ty<'tcx>, - ) -> Result<(), CopyImplementationError<'tcx>> { - // FIXME: (@jroesch) float this code up - tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.kind { - // These types used to have a builtin impl. - // Now libcore provides that impl. - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()), - - ty::Adt(adt, substs) => (adt, substs), - - _ => return Err(CopyImplementationError::NotAnAdt), - }; - - let mut infringing = Vec::new(); - for variant in &adt.variants { - for field in &variant.fields { - let ty = field.ty(tcx, substs); - if ty.references_error() { - continue; - } - let span = tcx.def_span(field.did); - let cause = ObligationCause { span, ..ObligationCause::dummy() }; - let ctx = traits::FulfillmentContext::new(); - match traits::fully_normalize(&infcx, ctx, cause, self, &ty) { - Ok(ty) => { - if !infcx.type_is_copy_modulo_regions(self, ty, span) { - infringing.push(field); - } - } - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - } - }; - } - } - if !infringing.is_empty() { - return Err(CopyImplementationError::InfrigingFields(infringing)); - } - if adt.has_dtor(tcx) { - return Err(CopyImplementationError::HasDestructor); - } - - Ok(()) - }) - } -} - impl<'tcx> TyCtxt<'tcx> { /// Creates a hash of the type `Ty` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. @@ -942,128 +874,9 @@ impl<'tcx> ty::TyS<'tcx> { } } -fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, lang_items::CopyTraitLangItem) -} - -fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, lang_items::SizedTraitLangItem) -} - -fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, lang_items::FreezeTraitLangItem) -} - -fn is_item_raw<'tcx>( - tcx: TyCtxt<'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - item: lang_items::LangItem, -) -> bool { - let (param_env, ty) = query.into_parts(); - let trait_def_id = tcx.require_lang_item(item, None); - tcx.infer_ctxt().enter(|infcx| { - traits::type_known_to_meet_bound_modulo_regions( - &infcx, - param_env, - ty, - trait_def_id, - DUMMY_SP, - ) - }) -} - #[derive(Clone, HashStable)] pub struct NeedsDrop(pub bool); -fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop { - let (param_env, ty) = query.into_parts(); - - let needs_drop = |ty: Ty<'tcx>| -> bool { tcx.needs_drop_raw(param_env.and(ty)).0 }; - - assert!(!ty.needs_infer()); - - NeedsDrop(match ty.kind { - // Fast-path for primitive types - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str => false, - - // Foreign types can never have destructors - ty::Foreign(..) => false, - - // `ManuallyDrop` doesn't have a destructor regardless of field types. - ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false, - - // Issue #22536: We first query `is_copy_modulo_regions`. It sees a - // normalized version of the type, and therefore will definitely - // know whether the type implements Copy (and thus needs no - // cleanup/drop/zeroing) ... - _ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false, - - // ... (issue #22536 continued) but as an optimization, still use - // prior logic of asking for the structural "may drop". - - // FIXME(#22815): Note that this is a conservative heuristic; - // it may report that the type "may drop" when actual type does - // not actually have a destructor associated with it. But since - // the type absolutely did not have the `Copy` bound attached - // (see above), it is sound to treat it as having a destructor. - - // User destructors are the only way to have concrete drop types. - ty::Adt(def, _) if def.has_dtor(tcx) => true, - - // Can refer to a type which may drop. - // FIXME(eddyb) check this against a ParamEnv. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Opaque(..) - | ty::Infer(_) - | ty::Error => true, - - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - - // Zero-length arrays never contain anything to drop. - ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false, - - // Structural recursion. - ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty), - - ty::Closure(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop) - } - - // Pessimistically assume that all generators will require destructors - // as we don't know if a destructor is a noop or not until after the MIR - // state transformation pass - ty::Generator(..) => true, - - ty::Tuple(..) => ty.tuple_fields().any(needs_drop), - - // unions don't have destructors because of the child types, - // only if they manually implement `Drop` (handled above). - ty::Adt(def, _) if def.is_union() => false, - - ty::Adt(def, substs) => def - .variants - .iter() - .any(|variant| variant.fields.iter().any(|field| needs_drop(field.ty(tcx, substs)))), - }) -} - pub enum ExplicitSelf<'tcx> { ByValue, ByReference(ty::Region<'tcx>, hir::Mutability), @@ -1112,13 +925,3 @@ impl<'tcx> ExplicitSelf<'tcx> { } } } - -pub fn provide(providers: &mut ty::query::Providers<'_>) { - *providers = ty::query::Providers { - is_copy_raw, - is_sized_raw, - is_freeze_raw, - needs_drop_raw, - ..*providers - }; -} diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 59b87afe216..e251e6d6aea 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -27,6 +27,7 @@ use lint::{EarlyContext, EarlyLintPass, LateLintPass, LintPass}; use lint::{LateContext, LintArray, LintContext}; use rustc::lint; use rustc::lint::FutureIncompatibleInfo; +use rustc::traits::drop::can_type_implement_copy; use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_feature::Stability; @@ -555,7 +556,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations { if ty.is_copy_modulo_regions(cx.tcx, param_env, item.span) { return; } - if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() { + if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() { cx.span_lint( MISSING_COPY_IMPLEMENTATIONS, item.span, diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 4c5b57791c5..35ef7ec148f 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -6,6 +6,7 @@ use rustc::lint; use rustc::middle::privacy::AccessLevels; use rustc::middle::stability::{DeprecationEntry, Index}; use rustc::session::Session; +use rustc::traits::drop::can_type_implement_copy; use rustc::ty::query::Providers; use rustc::ty::TyCtxt; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -488,7 +489,7 @@ impl Visitor<'tcx> for Checker<'tcx> { .emit(); } else { let param_env = self.tcx.param_env(def_id); - if !param_env.can_type_implement_copy(self.tcx, ty).is_ok() { + if !can_type_implement_copy(self.tcx, param_env, ty).is_ok() { feature_err( &self.tcx.sess.parse_sess, sym::untagged_unions, diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index 5a80b762f74..1bb512e350a 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -7,10 +7,10 @@ use rustc::middle::lang_items::UnsizeTraitLangItem; use rustc::middle::region; use rustc::infer; +use rustc::traits::drop::{can_type_implement_copy, CopyImplementationError}; use rustc::traits::predicate_for_trait_def; use rustc::traits::{self, ObligationCause, TraitEngine}; use rustc::ty::adjustment::CoerceUnsizedInfo; -use rustc::ty::util::CopyImplementationError; use rustc::ty::TypeFoldable; use rustc::ty::{self, Ty, TyCtxt}; @@ -92,7 +92,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: DefId) { debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); - match param_env.can_type_implement_copy(tcx, self_type) { + match can_type_implement_copy(tcx, param_env, self_type) { Ok(()) => {} Err(CopyImplementationError::InfrigingFields(fields)) => { let item = tcx.hir().expect_item(impl_hir_id); |
