From 62fba55670f3f54356cd757ac6d221be42745fad Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Wed, 18 Sep 2019 19:33:31 -0400
Subject: [PATCH 1/2] Make trivial dropck outlives a query

This allows caching some recursive types and getting to an error much
more quickly.
---
 src/librustc/query/mod.rs                     |  6 ++++++
 src/librustc/traits/query/dropck_outlives.rs  | 16 ++++++++++++----
 src/librustc/traits/query/type_op/outlives.rs |  3 +--
 src/librustc/ty/mod.rs                        |  1 +
 src/librustc_traits/dropck_outlives.rs        |  4 ++++
 5 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index c95652f274e36..2a012c5274191 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -231,6 +231,12 @@ rustc_queries! {
             cycle_delay_bug
         }
 
+        query trivial_dropck_outlives(ty: Ty<'tcx>) -> bool {
+            anon
+            no_force
+            desc { "checking if `{:?}` has trivial dropck", ty }
+        }
+
         query adt_dtorck_constraint(
             _: DefId
         ) -> Result<DtorckConstraint<'tcx>, NoSolution> {}
diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs
index eaf5971e4592f..e84c91daf293f 100644
--- a/src/librustc/traits/query/dropck_outlives.rs
+++ b/src/librustc/traits/query/dropck_outlives.rs
@@ -5,6 +5,7 @@ use std::iter::FromIterator;
 use syntax::source_map::Span;
 use crate::ty::subst::GenericArg;
 use crate::ty::{self, Ty, TyCtxt};
+use crate::ty::query::Providers;
 
 impl<'cx, 'tcx> At<'cx, 'tcx> {
     /// Given a type `ty` of some value being dropped, computes a set
@@ -33,7 +34,7 @@ impl<'cx, 'tcx> At<'cx, 'tcx> {
         // 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) {
+        if tcx.trivial_dropck_outlives(ty) {
             return InferOk {
                 value: vec![],
                 obligations: vec![],
@@ -207,15 +208,15 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
         | ty::Error => true,
 
         // [T; N] and [T] have same properties as T.
-        ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
+        ty::Array(ty, _) | ty::Slice(ty) => tcx.trivial_dropck_outlives(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())),
+        ty::Tuple(ref tys) => tys.iter().all(|t| tcx.trivial_dropck_outlives(t.expect_ty())),
         ty::Closure(def_id, ref substs) => substs
             .as_closure()
             .upvar_tys(def_id, tcx)
-            .all(|t| trivial_dropck_outlives(tcx, t)),
+            .all(|t| tcx.trivial_dropck_outlives(t)),
 
         ty::Adt(def, _) => {
             if Some(def.did) == tcx.lang_items().manually_drop() {
@@ -243,3 +244,10 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
         ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
     }
 }
+
+crate fn provide(p: &mut Providers<'_>) {
+    *p = Providers {
+        trivial_dropck_outlives,
+        ..*p
+    };
+}
diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs
index 9b956f3e55408..86a32d68fc09e 100644
--- a/src/librustc/traits/query/type_op/outlives.rs
+++ b/src/librustc/traits/query/type_op/outlives.rs
@@ -1,5 +1,4 @@
 use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
-use crate::traits::query::dropck_outlives::trivial_dropck_outlives;
 use crate::traits::query::dropck_outlives::DropckOutlivesResult;
 use crate::traits::query::Fallible;
 use crate::ty::{ParamEnvAnd, Ty, TyCtxt};
@@ -22,7 +21,7 @@ impl super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
         tcx: TyCtxt<'tcx>,
         key: &ParamEnvAnd<'tcx, Self>,
     ) -> Option<Self::QueryResponse> {
-        if trivial_dropck_outlives(tcx, key.value.dropped_ty) {
+        if tcx.trivial_dropck_outlives(key.value.dropped_ty) {
             Some(DropckOutlivesResult::default())
         } else {
             None
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index cfd859c33c2ef..01b7cda6fe914 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -3394,6 +3394,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
     layout::provide(providers);
     util::provide(providers);
     constness::provide(providers);
+    crate::traits::query::dropck_outlives::provide(providers);
     *providers = ty::query::Providers {
         asyncness,
         associated_item,
diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs
index 88e62db9a10d8..e751c94f23d11 100644
--- a/src/librustc_traits/dropck_outlives.rs
+++ b/src/librustc_traits/dropck_outlives.rs
@@ -166,6 +166,10 @@ fn dtorck_constraint_for_ty<'tcx>(
         });
     }
 
+    if tcx.trivial_dropck_outlives(ty) {
+        return Ok(DtorckConstraint::empty());
+    }
+
     let result = match ty.kind {
         ty::Bool
         | ty::Char

From 8de7fd884a9478e8fc41a810b9f4b647634efd0c Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Wed, 18 Sep 2019 21:48:46 -0400
Subject: [PATCH 2/2] Keep allocated vectors during dropck

Previously we'd frequently throw away vectors which is bad for
performance
---
 src/librustc_traits/dropck_outlives.rs | 111 +++++++++++--------------
 1 file changed, 50 insertions(+), 61 deletions(-)

diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs
index e751c94f23d11..c1316f415a559 100644
--- a/src/librustc_traits/dropck_outlives.rs
+++ b/src/librustc_traits/dropck_outlives.rs
@@ -80,22 +80,30 @@ fn dropck_outlives<'tcx>(
             let mut fulfill_cx = TraitEngine::new(infcx.tcx);
 
             let cause = ObligationCause::dummy();
+            let mut constraints = DtorckConstraint::empty();
             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)?;
+                info!("{} kinds, {} overflows, {} ty_stack",
+                    result.kinds.len(), result.overflows.len(), ty_stack.len());
+                dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
 
                 // "outlives" represent types/regions that may be touched
                 // by a destructor.
-                result.kinds.extend(outlives);
-                result.overflows.extend(overflows);
+                result.kinds.extend(constraints.outlives.drain(..));
+                result.overflows.extend(constraints.overflows.drain(..));
+
+                // If we have even one overflow, we should stop trying to evaluate further --
+                // chances are, the subsequent overflows for this evaluation won't provide useful
+                // information and will just decrease the speed at which we can emit these errors
+                // (since we'll be printing for just that much longer for the often enormous types
+                // that result here).
+                if result.overflows.len() >= 1 {
+                    break;
+                }
 
                 // 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 {
+                for ty in constraints.dtorck_types.drain(..) {
                     match infcx.at(&cause, param_env).normalize(&ty) {
                         Ok(Normalized {
                             value: ty,
@@ -152,25 +160,23 @@ fn dtorck_constraint_for_ty<'tcx>(
     for_ty: Ty<'tcx>,
     depth: usize,
     ty: Ty<'tcx>,
-) -> Result<DtorckConstraint<'tcx>, NoSolution> {
+    constraints: &mut DtorckConstraint<'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],
-        });
+        constraints.overflows.push(ty);
+        return Ok(());
     }
 
     if tcx.trivial_dropck_outlives(ty) {
-        return Ok(DtorckConstraint::empty());
+        return Ok(());
     }
 
-    let result = match ty.kind {
+    match ty.kind {
         ty::Bool
         | ty::Char
         | ty::Int(_)
@@ -185,22 +191,20 @@ fn dtorck_constraint_for_ty<'tcx>(
         | ty::FnPtr(_)
         | ty::GeneratorWitness(..) => {
             // these types never have a destructor
-            Ok(DtorckConstraint::empty())
         }
 
         ty::Array(ety, _) | ty::Slice(ety) => {
             // single-element containers, behave like their element
-            dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety)
+            dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints)?;
         }
 
-        ty::Tuple(tys) => tys.iter()
-            .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty.expect_ty()))
-            .collect(),
+        ty::Tuple(tys) => for ty in tys.iter() {
+            dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty.expect_ty(), constraints)?;
+        },
 
-        ty::Closure(def_id, substs) => substs.as_closure()
-            .upvar_tys(def_id, tcx)
-            .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
-            .collect(),
+        ty::Closure(def_id, substs) => for ty in substs.as_closure().upvar_tys(def_id, tcx) {
+            dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
+        }
 
         ty::Generator(def_id, substs, _movability) => {
             // rust-lang/rust#49918: types can be constructed, stored
@@ -226,17 +230,8 @@ fn dtorck_constraint_for_ty<'tcx>(
             // derived from lifetimes attached to the upvars, and we
             // *do* incorporate the upvars here.
 
-            let constraint = DtorckConstraint {
-                outlives: substs.as_generator().upvar_tys(def_id, tcx).map(|t| t.into()).collect(),
-                dtorck_types: vec![],
-                overflows: vec![],
-            };
-            debug!(
-                "dtorck_constraint: generator {:?} => {:?}",
-                def_id, constraint
-            );
-
-            Ok(constraint)
+            constraints.outlives.extend(substs.as_generator().upvar_tys(def_id, tcx)
+                .map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }));
         }
 
         ty::Adt(def, substs) => {
@@ -245,41 +240,34 @@ fn dtorck_constraint_for_ty<'tcx>(
                 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),
-            })
+            // FIXME: we can try to recursively `dtorck_constraint_on_ty`
+            // there, but that needs some way to handle cycles.
+            constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs));
+            constraints.outlives.extend(outlives.subst(tcx, substs));
+            constraints.overflows.extend(overflows.subst(tcx, substs));
         }
 
         // Objects must be alive in order for their destructor
         // to be called.
-        ty::Dynamic(..) => Ok(DtorckConstraint {
-            outlives: vec![ty.into()],
-            dtorck_types: vec![],
-            overflows: vec![],
-        }),
+        ty::Dynamic(..) => {
+            constraints.outlives.push(ty.into());
+        },
 
         // Types that can't be resolved. Pass them forward.
-        ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => Ok(DtorckConstraint {
-            outlives: vec![],
-            dtorck_types: vec![ty],
-            overflows: vec![],
-        }),
+        ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => {
+            constraints.dtorck_types.push(ty);
+        },
 
         ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
 
         ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => {
             // By the time this code runs, all type variables ought to
             // be fully resolved.
-            Err(NoSolution)
+            return Err(NoSolution)
         }
-    };
+    }
 
-    debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
-    result
+    Ok(())
 }
 
 /// Calculates the dtorck constraint for a type.
@@ -305,10 +293,11 @@ crate fn adt_dtorck_constraint(
         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>>()?;
+    let mut result = DtorckConstraint::empty();
+    for field in def.all_fields() {
+        let fty = tcx.type_of(field.did);
+        dtorck_constraint_for_ty(tcx, span, fty, 0, fty, &mut result)?;
+    }
     result.outlives.extend(tcx.destructor_constraints(def));
     dedup_dtorck_constraint(&mut result);