rust/src/librustc/traits/query/dropck_outlives.rs

209 lines
7.8 KiB
Rust
Raw Normal View History

2019-02-05 18:20:45 +01:00
use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues;
2019-12-22 23:42:04 +01:00
use crate::infer::InferOk;
use crate::ty::subst::GenericArg;
2019-02-05 18:20:45 +01:00
use crate::ty::{self, Ty, TyCtxt};
use rustc_span::source_map::Span;
2019-12-22 23:42:04 +01:00
use std::iter::FromIterator;
use rustc_error_codes::*;
2019-06-13 23:48:52 +02:00
impl<'cx, 'tcx> At<'cx, '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<GenericArg<'tcx>>> {
2019-12-22 23:42:04 +01:00
debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,);
// Quick check: there are a number of cases that we know do not require
// any destructor.
let tcx = self.infcx.tcx;
if trivial_dropck_outlives(tcx, ty) {
2019-12-22 23:42:04 +01:00
return InferOk { value: vec![], obligations: vec![] };
}
let mut orig_values = OriginalQueryValues::default();
Avoid most allocations in `Canonicalizer`. Extra allocations are a significant cost of NLL, and the most common ones come from within `Canonicalizer`. In particular, `canonical_var()` contains this code: indices .entry(kind) .or_insert_with(|| { let cvar1 = variables.push(info); let cvar2 = var_values.push(kind); assert_eq!(cvar1, cvar2); cvar1 }) .clone() `variables` and `var_values` are `Vec`s. `indices` is a `HashMap` used to track what elements have been inserted into `var_values`. If `kind` hasn't been seen before, `indices`, `variables` and `var_values` all get a new element. (The number of elements in each container is always the same.) This results in lots of allocations. In practice, most of the time these containers only end up holding a few elements. This PR changes them to avoid heap allocations in the common case, by changing the `Vec`s to `SmallVec`s and only using `indices` once enough elements are present. (When the number of elements is small, a direct linear search of `var_values` is as good or better than a hashmap lookup.) The changes to `variables` are straightforward and contained within `Canonicalizer`. The changes to `indices` are more complex but also contained within `Canonicalizer`. The changes to `var_values` are more intrusive because they require defining a new type `SmallCanonicalVarValues` -- which is to `CanonicalVarValues` as `SmallVec` is to `Vec -- and passing stack-allocated values of that type in from outside. All this speeds up a number of NLL "check" builds, the best by 2%.
2018-07-13 01:57:33 +02:00
let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values);
let span = self.cause.span;
debug!("c_ty = {:?}", c_ty);
if let Ok(result) = &tcx.dropck_outlives(c_ty) {
2018-12-19 16:39:01 +01:00
if result.is_proven() {
if let Ok(InferOk { value, obligations }) =
self.infcx.instantiate_query_response_and_region_obligations(
2019-12-22 23:42:04 +01:00
self.cause,
self.param_env,
&orig_values,
result,
)
{
let ty = self.infcx.resolve_vars_if_possible(&ty);
let kinds = value.into_kinds_reporting_overflows(tcx, span, ty);
2019-12-22 23:42:04 +01:00
return InferOk { value: kinds, obligations };
}
}
}
// 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
2018-08-19 15:30:23 +02:00
// Either of these should have created an error before.
2019-12-22 23:42:04 +01:00
tcx.sess.delay_span_bug(span, "dtorck encountered internal error");
2018-12-19 16:39:01 +01:00
2019-12-22 23:42:04 +01:00
InferOk { value: vec![], obligations: vec![] }
}
}
2019-11-09 23:17:42 +01:00
#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)]
pub struct DropckOutlivesResult<'tcx> {
pub kinds: Vec<GenericArg<'tcx>>,
pub overflows: Vec<Ty<'tcx>>,
}
impl<'tcx> DropckOutlivesResult<'tcx> {
2019-06-13 23:48:52 +02:00
pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
2018-12-19 16:39:01 +01:00
if let Some(overflow_ty) = self.overflows.iter().next() {
errors::struct_span_err!(
tcx.sess,
span,
E0320,
"overflow while adding drop-check rules for {}",
ty,
)
.note(&format!("overflowed on {}", overflow_ty))
.emit();
}
}
pub fn into_kinds_reporting_overflows(
self,
2019-06-13 23:48:52 +02:00
tcx: TyCtxt<'tcx>,
span: Span,
ty: Ty<'tcx>,
) -> Vec<GenericArg<'tcx>> {
self.report_overflows(tcx, span, ty);
let DropckOutlivesResult { kinds, overflows: _ } = self;
kinds
}
}
/// A set of constraints that need to be satisfied in order for
/// a type to be valid for destruction.
2019-11-09 23:17:42 +01:00
#[derive(Clone, Debug, HashStable)]
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::GenericArg<'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> {
2019-12-22 23:42:04 +01:00
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
}
}
/// This returns true if the type `ty` is "trivial" for
/// dropck-outlives -- that is, if it doesn't require any types to
/// outlive. This is similar but not *quite* the same as the
/// `needs_drop` test in the compiler already -- that is, for every
/// type T for which this function return true, needs-drop would
2019-02-08 14:53:55 +01:00
/// return `false`. But the reverse does not hold: in particular,
/// `needs_drop` returns false for `PhantomData`, but it is not
/// trivial for dropck-outlives.
///
/// Note also that `needs_drop` requires a "global" type (i.e., one
2018-11-12 19:05:20 +01:00
/// with erased regions), but this function does not.
2019-06-13 23:48:52 +02:00
pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
2019-09-16 20:08:35 +02:00
match ty.kind {
// None of these types have a destructor and hence they do not
// require anything in particular to outlive the dtor's
// execution.
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
| ty::Foreign(..)
| ty::Error => true,
// [T; N] and [T] have same properties as T.
ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
// (T1..Tn) and closures have same properties as T1..Tn --
// check if *any* of those are trivial.
ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
2019-12-22 23:42:04 +01:00
ty::Closure(def_id, ref substs) => {
substs.as_closure().upvar_tys(def_id, tcx).all(|t| trivial_dropck_outlives(tcx, t))
}
ty::Adt(def, _) => {
if Some(def.did) == tcx.lang_items().manually_drop() {
// `ManuallyDrop` never has a dtor.
true
} else {
// Other types might. Moreover, PhantomData doesn't
// have a dtor, but it is considered to own its
// content, so it is non-trivial. Unions can have `impl Drop`,
// and hence are non-trivial as well.
false
}
}
// The following *might* require a destructor: needs deeper inspection.
ty::Dynamic(..)
| ty::Projection(..)
| ty::Param(_)
| ty::Opaque(..)
| ty::Placeholder(..)
| ty::Infer(_)
2018-10-22 20:37:56 +02:00
| ty::Bound(..)
| ty::Generator(..) => false,
ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
}
}