introduce infcx.at(..).dropck_outlives(..) operaton [VIC]

Backed by a canonicalized query. This computes all the types/regions that need
to be live when the destructor runs (i.e., that the dtor may access).
This commit is contained in:
Niko Matsakis 2018-02-21 10:55:16 -05:00
parent 3a50b41da4
commit ca87d24467
17 changed files with 558 additions and 299 deletions

View File

@ -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>) },

View File

@ -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<StableHashingContext<'a>> for ty::CrateVariancesMap {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Kind<'tcx>>> {
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<Kind<'tcx>>,
pub overflows: Vec<Ty<'tcx>>,
}
/// 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<ty::subst::Kind<'tcx>>,
/// Types that could not be resolved: projections and params.
pub dtorck_types: Vec<Ty<'tcx>>,
/// 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<Ty<'tcx>>,
}
impl<'tcx> DtorckConstraint<'tcx> {
pub fn empty() -> DtorckConstraint<'tcx> {
DtorckConstraint {
outlives: vec![],
dtorck_types: vec![],
overflows: vec![],
}
}
}
impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> {
fn from_iter<I: IntoIterator<Item = DtorckConstraint<'tcx>>>(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<Canonical<'gcx, QueryResult<'gcx, DropckOutlivesResult<'gcx>>>>;
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
});

View File

@ -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;

View File

@ -106,7 +106,6 @@ pub struct GlobalArenas<'tcx> {
tables: TypedArena<ty::TypeckTables<'tcx>>,
/// miri allocations
const_allocs: TypedArena<interpret::Allocation>,
}
impl<'tcx> GlobalArenas<'tcx> {

View File

@ -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)

View File

@ -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
}
}

View File

@ -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<ty::Destructor>,
[] 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<DtorckConstraint<'tcx>, 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<Canonical<'tcx, QueryResult<'tcx, DropckOutlivesResult<'tcx>>>>,
NoSolution,
>,
[] fn substitute_normalize_and_test_predicates:
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,

View File

@ -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 |

View File

@ -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("<error>").as_str() }

View File

@ -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::<Result<DtorckConstraint, ErrorReported>>()
.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<Vec<DefId>> {
@ -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<Lrc<Vec<DefId>>>,
}
/// 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<ty::subst::Kind<'tcx>>,
/// Types that could not be resolved: projections and params.
pub dtorck_types: Vec<Ty<'tcx>>,
}
impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx>
{
fn from_iter<I: IntoIterator<Item=DtorckConstraint<'tcx>>>(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

View File

@ -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<ty::DtorckConstraint<'tcx>, 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
}

View File

@ -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 kind_constraints = self.cx
.fully_perform_op(location.at_self(), |cx| {
let span = cx.last_span;
let tcx = cx.infcx.tcx;
let mut selcx = traits::SelectionContext::new(cx.infcx);
let cause = cx.misc(cx.last_span);
let mut final_obligations = Vec::new();
let mut kind_constraints = Vec::new();
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 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);

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Rc<Canonical<'tcx, QueryResult<'tcx, DropckOutlivesResult<'tcx>>>>, 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<A>,
// }
//
// 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<A>)`. These will get
// pushed onto the stack. Eventually, expanding `Vec<A>` 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<DtorckConstraint<'tcx>, 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<DtorckConstraint<'tcx>, 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<DtorckConstraint, NoSolution>>()?;
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());
}

View File

@ -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
};

View File

@ -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(())
}

View File

@ -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 => {}
_ => {