diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index caef3a8acc44c..130fbc192677f 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -70,7 +70,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; -use traits::query::CanonicalProjectionGoal; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; @@ -637,6 +637,8 @@ define_dep_nodes!( <'tcx> [input] OutputFilenames, [anon] NormalizeTy, [] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>), + [] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>), + [] DropckOutlives(CanonicalTyGoal<'tcx>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index ae67d592ba30c..4eb4f0edafe40 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1017,12 +1017,6 @@ impl_stable_hash_for!(struct ty::Destructor { did }); -impl_stable_hash_for!(struct ty::DtorckConstraint<'tcx> { - outlives, - dtorck_types -}); - - impl<'a> HashStable> for ty::CrateVariancesMap { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs new file mode 100644 index 0000000000000..5c964f6559a3f --- /dev/null +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -0,0 +1,193 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use infer::at::At; +use infer::canonical::{Canonical, Canonicalize, QueryResult}; +use infer::InferOk; +use std::iter::FromIterator; +use traits::query::CanonicalTyGoal; +use ty::{self, Ty, TyCtxt}; +use ty::subst::Kind; +use std::rc::Rc; + +impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { + /// Given a type `ty` of some value being dropped, computes a set + /// of "kinds" (types, regions) that must be outlive the execution + /// of the destructor. These basically correspond to data that the + /// destructor might access. This is used during regionck to + /// impose "outlives" constraints on any lifetimes referenced + /// within. + /// + /// The rules here are given by the "dropck" RFCs, notably [#1238] + /// and [#1327]. This is a fixed-point computation, where we + /// explore all the data that will be dropped (transitively) when + /// a value of type `ty` is dropped. For each type T that will be + /// dropped and which has a destructor, we must assume that all + /// the types/regions of T are live during the destructor, unless + /// they are marked with a special attribute (`#[may_dangle]`). + /// + /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md + /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md + pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { + debug!( + "dropck_outlives(ty={:?}, param_env={:?})", + ty, self.param_env, + ); + + let tcx = self.infcx.tcx; + let gcx = tcx.global_tcx(); + let (c_ty, orig_values) = self.infcx.canonicalize_query(&self.param_env.and(ty)); + let span = self.cause.span; + match &gcx.dropck_outlives(c_ty) { + Ok(result) if result.is_proven() => { + match self.infcx.instantiate_query_result( + self.cause, + self.param_env, + &orig_values, + result, + ) { + Ok(InferOk { + value: DropckOutlivesResult { kinds, overflows }, + obligations, + }) => { + for overflow_ty in overflows.into_iter().take(1) { + let mut err = struct_span_err!( + tcx.sess, + span, + E0320, + "overflow while adding drop-check rules for {}", + self.infcx.resolve_type_vars_if_possible(&ty), + ); + err.note(&format!("overflowed on {}", overflow_ty)); + err.emit(); + } + + return InferOk { + value: kinds, + obligations, + }; + } + + Err(_) => { /* fallthrough to error-handling code below */ } + } + } + + _ => { /* fallthrough to error-handling code below */ } + } + + // Errors and ambiuity in dropck occur in two cases: + // - unresolved inference variables at the end of typeck + // - non well-formed types where projections cannot be resolved + // Either of these should hvae created an error before. + tcx.sess + .delay_span_bug(span, "dtorck encountered internal error"); + return InferOk { + value: vec![], + obligations: vec![], + }; + } +} + +#[derive(Clone, Debug)] +pub struct DropckOutlivesResult<'tcx> { + pub kinds: Vec>, + pub overflows: Vec>, +} + +/// A set of constraints that need to be satisfied in order for +/// a type to be valid for destruction. +#[derive(Clone, Debug)] +pub struct DtorckConstraint<'tcx> { + /// Types that are required to be alive in order for this + /// type to be valid for destruction. + pub outlives: Vec>, + + /// Types that could not be resolved: projections and params. + pub dtorck_types: Vec>, + + /// If, during the computation of the dtorck constraint, we + /// overflow, that gets recorded here. The caller is expected to + /// report an error. + pub overflows: Vec>, +} + +impl<'tcx> DtorckConstraint<'tcx> { + pub fn empty() -> DtorckConstraint<'tcx> { + DtorckConstraint { + outlives: vec![], + dtorck_types: vec![], + overflows: vec![], + } + } +} + +impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { + fn from_iter>>(iter: I) -> Self { + let mut result = Self::empty(); + + for DtorckConstraint { + outlives, + dtorck_types, + overflows, + } in iter + { + result.outlives.extend(outlives); + result.dtorck_types.extend(dtorck_types); + result.overflows.extend(overflows); + } + + result + } +} +impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, Ty<'tcx>> { + type Canonicalized = CanonicalTyGoal<'gcx>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + value + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for DropckOutlivesResult<'tcx> { + kinds, overflows + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for DropckOutlivesResult<'a> { + type Lifted = DropckOutlivesResult<'tcx>; + kinds, overflows + } +} + +impl_stable_hash_for!(struct DropckOutlivesResult<'tcx> { + kinds, overflows +}); + +impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, DropckOutlivesResult<'tcx>> { + // we ought to intern this, but I'm too lazy just now + type Canonicalized = Rc>>>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + Rc::new(value) + } +} + +impl_stable_hash_for!(struct DtorckConstraint<'tcx> { + outlives, + dtorck_types, + overflows +}); diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index bba2c1555832e..607344f9c6781 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -16,13 +16,16 @@ //! `librustc_traits`. use infer::canonical::Canonical; -use ty; +use ty::{self, Ty}; +pub mod dropck_outlives; pub mod normalize; pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; +pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index d85a95e87ea8e..24d3b37f804ee 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -106,7 +106,6 @@ pub struct GlobalArenas<'tcx> { tables: TypedArena>, /// miri allocations const_allocs: TypedArena, - } impl<'tcx> GlobalArenas<'tcx> { diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 95865d5eab0bc..bcd6a5ace627d 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -11,7 +11,7 @@ use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId}; -use traits::query::CanonicalProjectionGoal; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::maps::queries; @@ -61,6 +61,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::normalize_projection_ty<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::dropck_outlives<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTyGoal<'tcx>) -> String { + format!("computing dropck types for `{:?}`", goal) + } +} + impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is `Copy`", env.value) diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index 1f040522fda98..b8167ec918638 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -11,7 +11,7 @@ //! Defines the set of legal keys that can be used in queries. use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; -use traits::query::CanonicalProjectionGoal; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; @@ -181,3 +181,13 @@ impl<'tcx> Key for CanonicalProjectionGoal<'tcx> { DUMMY_SP } } + +impl<'tcx> Key for CanonicalTyGoal<'tcx> { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, _tcx: TyCtxt) -> Span { + DUMMY_SP + } +} diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 2dc6cd7a4eb41..4df15b2e76be8 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -34,7 +34,8 @@ use mir::interpret::{GlobalId}; use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::Vtable; -use traits::query::{CanonicalProjectionGoal, NoSolution}; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal, NoSolution}; +use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; @@ -114,7 +115,9 @@ define_maps! { <'tcx> [] fn adt_def: AdtDefOfItem(DefId) -> &'tcx ty::AdtDef, [] fn adt_destructor: AdtDestructor(DefId) -> Option, [] fn adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>], - [] fn adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>, + [] fn adt_dtorck_constraint: DtorckConstraint( + DefId + ) -> Result, NoSolution>, /// True if this is a const fn [] fn is_const_fn: IsConstFn(DefId) -> bool, @@ -391,6 +394,14 @@ define_maps! { <'tcx> NoSolution, >, + /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. + [] fn dropck_outlives: DropckOutlives( + CanonicalTyGoal<'tcx> + ) -> Result< + Lrc>>>, + NoSolution, + >, + [] fn substitute_normalize_and_test_predicates: substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool, diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 2124b6296aab1..64b17922049a1 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -774,6 +774,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::EraseRegionsTy | DepKind::NormalizeTy | DepKind::NormalizeProjectionTy | + DepKind::DropckOutlives | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | diff --git a/src/librustc/ty/maps/values.rs b/src/librustc/ty/maps/values.rs index 165798d19f196..8d38d7dbbbbff 100644 --- a/src/librustc/ty/maps/values.rs +++ b/src/librustc/ty/maps/values.rs @@ -35,12 +35,6 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> { } } -impl<'tcx> Value<'tcx> for ty::DtorckConstraint<'tcx> { - fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self { - Self::empty() - } -} - impl<'tcx> Value<'tcx> for ty::SymbolName { fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self { ty::SymbolName { name: Symbol::intern("").as_str() } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 93d1585cdc83d..fc1d26b0e0910 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -34,15 +34,13 @@ use ty; use ty::subst::{Subst, Substs}; use ty::util::{IntTypeExt, Discr}; use ty::walk::TypeWalker; -use util::common::ErrorReported; -use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet}; +use util::nodemap::{NodeSet, DefIdMap, FxHashMap}; use serialize::{self, Encodable, Encoder}; use std::cell::RefCell; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; use std::ops::Deref; use rustc_data_structures::sync::Lrc; use std::slice; @@ -2661,38 +2659,6 @@ fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result } -/// Calculates the dtorck constraint for a type. -fn adt_dtorck_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> DtorckConstraint<'tcx> { - let def = tcx.adt_def(def_id); - let span = tcx.def_span(def_id); - debug!("dtorck_constraint: {:?}", def); - - if def.is_phantom_data() { - let result = DtorckConstraint { - outlives: vec![], - dtorck_types: vec![ - tcx.mk_param_from_def(&tcx.generics_of(def_id).types[0]) - ] - }; - debug!("dtorck_constraint: {:?} => {:?}", def, result); - return result; - } - - let mut result = def.all_fields() - .map(|field| tcx.type_of(field.did)) - .map(|fty| tcx.dtorck_constraint_for_ty(span, fty, 0, fty)) - .collect::>() - .unwrap_or(DtorckConstraint::empty()); - result.outlives.extend(tcx.destructor_constraints(def)); - result.dedup(); - - debug!("dtorck_constraint: {:?} => {:?}", def, result); - - result -} - fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Lrc> { @@ -2808,7 +2774,6 @@ pub fn provide(providers: &mut ty::maps::Providers) { associated_item, associated_item_def_ids, adt_sized_constraint, - adt_dtorck_constraint, def_span, param_env, trait_of_item, @@ -2831,49 +2796,6 @@ pub struct CrateInherentImpls { pub inherent_impls: DefIdMap>>, } -/// A set of constraints that need to be satisfied in order for -/// a type to be valid for destruction. -#[derive(Clone, Debug)] -pub struct DtorckConstraint<'tcx> { - /// Types that are required to be alive in order for this - /// type to be valid for destruction. - pub outlives: Vec>, - /// Types that could not be resolved: projections and params. - pub dtorck_types: Vec>, -} - -impl<'tcx> FromIterator> for DtorckConstraint<'tcx> -{ - fn from_iter>>(iter: I) -> Self { - let mut result = Self::empty(); - - for constraint in iter { - result.outlives.extend(constraint.outlives); - result.dtorck_types.extend(constraint.dtorck_types); - } - - result - } -} - - -impl<'tcx> DtorckConstraint<'tcx> { - fn empty() -> DtorckConstraint<'tcx> { - DtorckConstraint { - outlives: vec![], - dtorck_types: vec![] - } - } - - fn dedup<'a>(&mut self) { - let mut outlives = FxHashSet(); - let mut dtorck_types = FxHashSet(); - - self.outlives.retain(|&val| outlives.replace(val).is_none()); - self.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none()); - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub struct SymbolName { // FIXME: we don't rely on interning or equality here - better have diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index e8ce49d39d299..753f89d8cd293 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -19,7 +19,7 @@ use middle::const_val::ConstVal; use traits; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::TypeVisitor; -use ty::subst::{Subst, UnpackedKind}; +use ty::subst::UnpackedKind; use ty::maps::TyCtxtAt; use ty::TypeVariants::*; use ty::layout::Integer; @@ -537,99 +537,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { result } - /// Return a set of constraints that needs to be satisfied in - /// order for `ty` to be valid for destruction. - pub fn dtorck_constraint_for_ty(self, - span: Span, - for_ty: Ty<'tcx>, - depth: usize, - ty: Ty<'tcx>) - -> Result, ErrorReported> - { - debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", - span, for_ty, depth, ty); - - if depth >= self.sess.recursion_limit.get() { - let mut err = struct_span_err!( - self.sess, span, E0320, - "overflow while adding drop-check rules for {}", for_ty); - err.note(&format!("overflowed on {}", ty)); - err.emit(); - return Err(ErrorReported); - } - - let result = match ty.sty { - ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | - ty::TyFloat(_) | ty::TyStr | ty::TyNever | ty::TyForeign(..) | - ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) | - ty::TyGeneratorWitness(..) => { - // these types never have a destructor - Ok(ty::DtorckConstraint::empty()) - } - - ty::TyArray(ety, _) | ty::TySlice(ety) => { - // single-element containers, behave like their element - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ety) - } - - ty::TyTuple(tys, _) => { - tys.iter().map(|ty| { - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) - }).collect() - } - - ty::TyClosure(def_id, substs) => { - substs.upvar_tys(def_id, self).map(|ty| { - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) - }).collect() - } - - ty::TyGenerator(def_id, substs, _) => { - // Note that the interior types are ignored here. - // Any type reachable inside the interior must also be reachable - // through the upvars. - substs.upvar_tys(def_id, self).map(|ty| { - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) - }).collect() - } - - ty::TyAdt(def, substs) => { - let ty::DtorckConstraint { - dtorck_types, outlives - } = self.at(span).adt_dtorck_constraint(def.did); - Ok(ty::DtorckConstraint { - // FIXME: we can try to recursively `dtorck_constraint_on_ty` - // there, but that needs some way to handle cycles. - dtorck_types: dtorck_types.subst(self, substs), - outlives: outlives.subst(self, substs) - }) - } - - // Objects must be alive in order for their destructor - // to be called. - ty::TyDynamic(..) => Ok(ty::DtorckConstraint { - outlives: vec![ty.into()], - dtorck_types: vec![], - }), - - // Types that can't be resolved. Pass them forward. - ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => { - Ok(ty::DtorckConstraint { - outlives: vec![], - dtorck_types: vec![ty], - }) - } - - ty::TyInfer(..) | ty::TyError => { - self.sess.delay_span_bug(span, "unresolved type in dtorck"); - Err(ErrorReported) - } - }; - - debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result); - result - } - pub fn is_closure(self, def_id: DefId) -> bool { self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr } diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index 5b3f439e0ebb9..d19fd2bb5969d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -14,13 +14,9 @@ use dataflow::MaybeInitializedPlaces; use dataflow::move_paths::{HasMoveData, MoveData}; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::mir::Local; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::traits; +use rustc::ty::{Ty, TyCtxt, TypeFoldable}; use rustc::infer::InferOk; -use rustc::util::common::ErrorReported; use borrow_check::nll::type_check::AtLocation; -use rustc_data_structures::fx::FxHashSet; -use syntax::codemap::DUMMY_SP; use util::liveness::LivenessResults; use super::TypeChecker; @@ -193,73 +189,34 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo // // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization // ourselves in one large 'fully_perform_op' callback. - let (type_constraints, kind_constraints) = self.cx.fully_perform_op(location.at_self(), - |cx| { - - let tcx = cx.infcx.tcx; - let mut selcx = traits::SelectionContext::new(cx.infcx); - let cause = cx.misc(cx.last_span); - - let mut types = vec![(dropped_ty, 0)]; - let mut final_obligations = Vec::new(); - let mut type_constraints = Vec::new(); - let mut kind_constraints = Vec::new(); - - let mut known = FxHashSet(); - - while let Some((ty, depth)) = types.pop() { - let span = DUMMY_SP; // FIXME - let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { - Ok(result) => result, - Err(ErrorReported) => { - continue; - } - }; - - let ty::DtorckConstraint { - outlives, - dtorck_types, - } = result; - - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - for outlive in outlives { + let kind_constraints = self.cx + .fully_perform_op(location.at_self(), |cx| { + let span = cx.last_span; + + let mut final_obligations = Vec::new(); + let mut kind_constraints = Vec::new(); + + let InferOk { + value: kinds, + obligations, + } = cx.infcx + .at(&cx.misc(span), cx.param_env) + .dropck_outlives(dropped_ty); + for kind in kinds { + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. let cause = Cause::DropVar(dropped_local, location); - kind_constraints.push((outlive, location, cause)); + kind_constraints.push((kind, location, cause)); } - // However, there may also be some types that - // `dtorck_constraint_for_ty` could not resolve (e.g., - // associated types and parameters). We need to normalize - // associated types here and possibly recursively process. - for ty in dtorck_types { - let traits::Normalized { value: ty, obligations } = - traits::normalize(&mut selcx, cx.param_env, cause.clone(), &ty); - - final_obligations.extend(obligations); + final_obligations.extend(obligations); - let ty = cx.infcx.resolve_type_and_region_vars_if_possible(&ty); - match ty.sty { - ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { - let cause = Cause::DropVar(dropped_local, location); - type_constraints.push((ty, location, cause)); - } - - _ => if known.insert(ty) { - types.push((ty, depth + 1)); - }, - } - } - } - - Ok(InferOk { - value: (type_constraints, kind_constraints), obligations: final_obligations + Ok(InferOk { + value: kind_constraints, + obligations: final_obligations, + }) }) - }).unwrap(); - - for (ty, location, cause) in type_constraints { - self.push_type_live_constraint(ty, location, cause); - } + .unwrap(); for (kind, location, cause) in kind_constraints { self.push_type_live_constraint(kind, location, cause); diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs new file mode 100644 index 0000000000000..2274f3942bdbc --- /dev/null +++ b/src/librustc_traits/dropck_outlives.rs @@ -0,0 +1,285 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::infer::canonical::{Canonical, QueryResult}; +use rustc::hir::def_id::DefId; +use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; +use rustc::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc::traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; +use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc::ty::subst::Subst; +use rustc::util::nodemap::FxHashSet; +use std::rc::Rc; +use syntax::codemap::{Span, DUMMY_SP}; +use util; + +crate fn dropck_outlives<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + goal: CanonicalTyGoal<'tcx>, +) -> Result>>>, NoSolution> { + debug!("dropck_outlives(goal={:#?})", goal); + + tcx.infer_ctxt().enter(|ref infcx| { + let tcx = infcx.tcx; + let ( + ParamEnvAnd { + param_env, + value: for_ty, + }, + canonical_inference_vars, + ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal); + + let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + + // A stack of types left to process. Each round, we pop + // something from the stack and invoke + // `dtorck_constraint_for_ty`. This may produce new types that + // have to be pushed on the stack. This continues until we have explored + // all the reachable types from the type `for_ty`. + // + // Example: Imagine that we have the following code: + // + // ```rust + // struct A { + // value: B, + // children: Vec, + // } + // + // struct B { + // value: u32 + // } + // + // fn f() { + // let a: A = ...; + // .. + // } // here, `a` is dropped + // ``` + // + // at the point where `a` is dropped, we need to figure out + // which types inside of `a` contain region data that may be + // accessed by any destructors in `a`. We begin by pushing `A` + // onto the stack, as that is the type of `a`. We will then + // invoke `dtorck_constraint_for_ty` which will expand `A` + // into the types of its fields `(B, Vec)`. These will get + // pushed onto the stack. Eventually, expanding `Vec` will + // lead to us trying to push `A` a second time -- to prevent + // infinite recusion, we notice that `A` was already pushed + // once and stop. + let mut ty_stack = vec![(for_ty, 0)]; + + // Set used to detect infinite recursion. + let mut ty_set = FxHashSet(); + + let fulfill_cx = &mut FulfillmentContext::new(); + + let cause = ObligationCause::dummy(); + while let Some((ty, depth)) = ty_stack.pop() { + let DtorckConstraint { + dtorck_types, + outlives, + overflows, + } = dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty)?; + + // "outlives" represent types/regions that may be touched + // by a destructor. + result.kinds.extend(outlives); + result.overflows.extend(overflows); + + // dtorck types are "types that will get dropped but which + // do not themselves define a destructor", more or less. We have + // to push them onto the stack to be expanded. + for ty in dtorck_types { + match infcx.at(&cause, param_env).normalize(&ty) { + Ok(Normalized { + value: ty, + obligations, + }) => { + fulfill_cx.register_predicate_obligations(infcx, obligations); + + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + + match ty.sty { + // All parameters live for the duration of the + // function. + ty::TyParam(..) => {} + + // A projection that we couldn't resolve - it + // might have a destructor. + ty::TyProjection(..) | ty::TyAnon(..) => { + result.kinds.push(ty.into()); + } + + _ => { + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); + } + } + } + } + + // We don't actually expect to fail to normalize. + // That implies a WF error somewhere else. + Err(NoSolution) => { + return Err(NoSolution); + } + } + } + } + + debug!("dropck_outlives: result = {:#?}", result); + + util::make_query_response(infcx, canonical_inference_vars, result, fulfill_cx) + }) +} + +/// Return a set of constraints that needs to be satisfied in +/// order for `ty` to be valid for destruction. +fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + span: Span, + for_ty: Ty<'tcx>, + depth: usize, + ty: Ty<'tcx>, +) -> Result, NoSolution> { + debug!( + "dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", + span, for_ty, depth, ty + ); + + if depth >= tcx.sess.recursion_limit.get() { + return Ok(DtorckConstraint { + outlives: vec![], + dtorck_types: vec![], + overflows: vec![ty], + }); + } + + let result = match ty.sty { + ty::TyBool + | ty::TyChar + | ty::TyInt(_) + | ty::TyUint(_) + | ty::TyFloat(_) + | ty::TyStr + | ty::TyNever + | ty::TyForeign(..) + | ty::TyRawPtr(..) + | ty::TyRef(..) + | ty::TyFnDef(..) + | ty::TyFnPtr(_) + | ty::TyGeneratorWitness(..) => { + // these types never have a destructor + Ok(DtorckConstraint::empty()) + } + + ty::TyArray(ety, _) | ty::TySlice(ety) => { + // single-element containers, behave like their element + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety) + } + + ty::TyTuple(tys, _) => tys.iter() + .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) + .collect(), + + ty::TyClosure(def_id, substs) => substs + .upvar_tys(def_id, tcx) + .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) + .collect(), + + ty::TyGenerator(def_id, substs, _) => { + // Note that the interior types are ignored here. + // Any type reachable inside the interior must also be reachable + // through the upvars. + substs + .upvar_tys(def_id, tcx) + .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) + .collect() + } + + ty::TyAdt(def, substs) => { + let DtorckConstraint { + dtorck_types, + outlives, + overflows, + } = tcx.at(span).adt_dtorck_constraint(def.did)?; + Ok(DtorckConstraint { + // FIXME: we can try to recursively `dtorck_constraint_on_ty` + // there, but that needs some way to handle cycles. + dtorck_types: dtorck_types.subst(tcx, substs), + outlives: outlives.subst(tcx, substs), + overflows: overflows.subst(tcx, substs), + }) + } + + // Objects must be alive in order for their destructor + // to be called. + ty::TyDynamic(..) => Ok(DtorckConstraint { + outlives: vec![ty.into()], + dtorck_types: vec![], + overflows: vec![], + }), + + // Types that can't be resolved. Pass them forward. + ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => Ok(DtorckConstraint { + outlives: vec![], + dtorck_types: vec![ty], + overflows: vec![], + }), + + ty::TyInfer(..) | ty::TyError => { + // By the time this code runs, all type variables ought to + // be fully resolved. + Err(NoSolution) + } + }; + + debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result); + result +} + +/// Calculates the dtorck constraint for a type. +crate fn adt_dtorck_constraint<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, +) -> Result, NoSolution> { + let def = tcx.adt_def(def_id); + let span = tcx.def_span(def_id); + debug!("dtorck_constraint: {:?}", def); + + if def.is_phantom_data() { + let result = DtorckConstraint { + outlives: vec![], + dtorck_types: vec![tcx.mk_param_from_def(&tcx.generics_of(def_id).types[0])], + overflows: vec![], + }; + debug!("dtorck_constraint: {:?} => {:?}", def, result); + return Ok(result); + } + + let mut result = def.all_fields() + .map(|field| tcx.type_of(field.did)) + .map(|fty| dtorck_constraint_for_ty(tcx, span, fty, 0, fty)) + .collect::>()?; + result.outlives.extend(tcx.destructor_constraints(def)); + dedup_dtorck_constraint(&mut result); + + debug!("dtorck_constraint: {:?} => {:?}", def, result); + + Ok(result) +} + +fn dedup_dtorck_constraint<'tcx>(c: &mut DtorckConstraint<'tcx>) { + let mut outlives = FxHashSet(); + let mut dtorck_types = FxHashSet(); + + c.outlives.retain(|&val| outlives.replace(val).is_none()); + c.dtorck_types + .retain(|&val| dtorck_types.replace(val).is_none()); +} diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 0d92404d24b08..59083dcfbf0f4 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -24,6 +24,7 @@ extern crate rustc_data_structures; extern crate syntax; extern crate syntax_pos; +mod dropck_outlives; mod normalize_projection_ty; mod util; @@ -31,6 +32,8 @@ use rustc::ty::maps::Providers; pub fn provide(p: &mut Providers) { *p = Providers { + dropck_outlives: dropck_outlives::dropck_outlives, + adt_dtorck_constraint: dropck_outlives::adt_dtorck_constraint, normalize_projection_ty: normalize_projection_ty::normalize_projection_ty, ..*p }; diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index f33536227a0ba..67c9832cbf9f4 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -18,8 +18,8 @@ use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::traits::{self, ObligationCause}; use util::common::ErrorReported; -use util::nodemap::FxHashSet; +use syntax::ast; use syntax_pos::Span; /// check_drop_impl confirms that the Drop implementation identified by @@ -282,6 +282,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>, ty: Ty<'tcx>, span: Span, + body_id: ast::NodeId, scope: region::Scope) -> Result<(), ErrorReported> { @@ -297,46 +298,15 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( }; let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope)); let origin = || infer::SubregionOrigin::SafeDestructor(span); - - let ty = rcx.fcx.resolve_type_vars_if_possible(&ty); - let for_ty = ty; - let mut types = vec![(ty, 0)]; - let mut known = FxHashSet(); - while let Some((ty, depth)) = types.pop() { - let ty::DtorckConstraint { - dtorck_types, outlives - } = rcx.tcx.dtorck_constraint_for_ty(span, for_ty, depth, ty)?; - - for ty in dtorck_types { - let ty = rcx.fcx.normalize_associated_types_in(span, &ty); - let ty = rcx.fcx.resolve_type_vars_with_obligations(ty); - let ty = rcx.fcx.resolve_type_and_region_vars_if_possible(&ty); - match ty.sty { - // All parameters live for the duration of the - // function. - ty::TyParam(..) => {} - - // A projection that we couldn't resolve - it - // might have a destructor. - ty::TyProjection(..) | ty::TyAnon(..) => { - rcx.type_must_outlive(origin(), ty, parent_scope); - } - - _ => { - if let None = known.replace(ty) { - types.push((ty, depth+1)); - } - } - } - } - - for outlive in outlives { - match outlive.unpack() { - UnpackedKind::Lifetime(lt) => rcx.sub_regions(origin(), parent_scope, lt), - UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope), - } + let cause = &ObligationCause::misc(span, body_id); + let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty); + debug!("dropck_outlives = {:#?}", infer_ok); + let kinds = rcx.fcx.register_infer_ok_obligations(infer_ok); + for kind in kinds { + match kind.unpack() { + UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope, r), + UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope), } } - Ok(()) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index cfe8aa99bfa08..9ed4ab45a1ba7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -411,8 +411,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_of_node_must_outlive(origin, hir_id, var_region); let typ = self.resolve_node_type(hir_id); + let body_id = self.body_id; let _ = dropck::check_safety_of_destructor_if_necessary( - self, typ, span, var_scope); + self, typ, span, body_id, var_scope); }) } } @@ -884,8 +885,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { match *region { ty::ReScope(rvalue_scope) => { let typ = self.resolve_type(cmt.ty); + let body_id = self.body_id; let _ = dropck::check_safety_of_destructor_if_necessary( - self, typ, span, rvalue_scope); + self, typ, span, body_id, rvalue_scope); } ty::ReStatic => {} _ => {