Skip to content

Commit

Permalink
introduce infcx.at(..).dropck_outlives(..) operaton [VIC]
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
nikomatsakis committed Mar 13, 2018
1 parent 3a50b41 commit ca87d24
Show file tree
Hide file tree
Showing 17 changed files with 560 additions and 301 deletions.
4 changes: 3 additions & 1 deletion src/librustc/dep_graph/dep_node.rs
Expand Up @@ -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;

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

Expand Down
6 changes: 0 additions & 6 deletions src/librustc/ich/impls_ty.rs
Expand Up @@ -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>,
Expand Down
193 changes: 193 additions & 0 deletions 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 <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
});
5 changes: 4 additions & 1 deletion src/librustc/traits/query/mod.rs
Expand Up @@ -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;

Expand Down
1 change: 0 additions & 1 deletion src/librustc/ty/context.rs
Expand Up @@ -106,7 +106,6 @@ pub struct GlobalArenas<'tcx> {
tables: TypedArena<ty::TypeckTables<'tcx>>,
/// miri allocations
const_allocs: TypedArena<interpret::Allocation>,

}

impl<'tcx> GlobalArenas<'tcx> {
Expand Down
8 changes: 7 additions & 1 deletion src/librustc/ty/maps/config.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 11 additions & 1 deletion src/librustc/ty/maps/keys.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
}
15 changes: 13 additions & 2 deletions src/librustc/ty/maps/mod.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,

Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/maps/plumbing.rs
Expand Up @@ -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 |

Expand Down
6 changes: 0 additions & 6 deletions src/librustc/ty/maps/values.rs
Expand Up @@ -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() }
Expand Down

0 comments on commit ca87d24

Please sign in to comment.