From 909b10c33ba51ac704fa909395c58de3e4aca71f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:09:28 -0400 Subject: [PATCH 01/72] introduce `type_op` --- .../nll/type_check/input_output.rs | 9 ++-- .../borrow_check/nll/type_check/liveness.rs | 5 ++- .../borrow_check/nll/type_check/mod.rs | 26 +++++------ .../borrow_check/nll/type_check/type_op.rs | 43 +++++++++++++++++++ 4 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op.rs diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index d44eed65201cd..be0a2494b04fc 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -18,6 +18,7 @@ //! contain revealed `impl Trait` values). use borrow_check::nll::renumber; +use borrow_check::nll::type_check::type_op::CustomTypeOp; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferOk; @@ -80,7 +81,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( Locations::All, || format!("input_output"), - |cx| { + CustomTypeOp::new(|cx| { let mut obligations = ObligationAccumulator::default(); let dummy_body_id = ObligationCause::dummy().body_id; @@ -135,7 +136,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { value: Some(anon_type_map), obligations: obligations.into_vec(), }) - }, + }), ).unwrap_or_else(|terr| { span_mirbug!( self, @@ -156,13 +157,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( Locations::All, || format!("anon_type_map"), - |_cx| { + CustomTypeOp::new(|_cx| { infcx.constrain_anon_types(&anon_type_map, universal_regions); Ok(InferOk { value: (), obligations: vec![], }) - }, + }), ).unwrap(); } } 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 80f5fe4184f9d..d58dc1a601a7f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -10,6 +10,7 @@ use borrow_check::nll::region_infer::Cause; use borrow_check::nll::type_check::AtLocation; +use borrow_check::nll::type_check::type_op::CustomTypeOp; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; @@ -220,12 +221,12 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo let (dropped_kinds, region_constraint_data) = cx.fully_perform_op_and_get_region_constraint_data( || format!("compute_drop_data(dropped_ty={:?})", dropped_ty), - |cx| { + CustomTypeOp::new(|cx| { Ok(cx .infcx .at(&ObligationCause::dummy(), cx.param_env) .dropck_outlives(dropped_ty)) - }, + }), ).unwrap(); DropData { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d25cec7979140..5d06513e2a858 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -15,13 +15,14 @@ use borrow_check::location::LocationTable; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::Cause; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, OutlivesConstraint, TypeTest}; +use borrow_check::nll::type_check::type_op::{CustomTypeOp, TypeOp}; use borrow_check::nll::universal_regions::UniversalRegions; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; use rustc::hir::def_id::DefId; use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; -use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, UnitResult}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; @@ -67,6 +68,7 @@ macro_rules! span_mirbug_and_err { mod constraint_conversion; mod input_output; mod liveness; +mod type_op; /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to @@ -732,7 +734,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { &mut self, locations: Locations, describe_op: impl Fn() -> String, - op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>, + op: impl TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data( || format!("{} at {:?}", describe_op(), locations), @@ -777,7 +779,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn fully_perform_op_and_get_region_constraint_data( &mut self, describe_op: impl Fn() -> String, - op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>, + op: impl TypeOp<'gcx, 'tcx, Output = R>, ) -> Result<(R, Option>>), TypeError<'tcx>> { if cfg!(debug_assertions) { info!( @@ -788,7 +790,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let mut fulfill_cx = TraitEngine::new(self.infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; - let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; + let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op.perform(self))?; debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); fulfill_cx.register_predicate_obligations(self.infcx, obligations); if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { @@ -824,11 +826,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( locations, || format!("sub_types({:?} <: {:?})", sub, sup), - |this| { + CustomTypeOp::new(|this| { this.infcx .at(&ObligationCause::dummy(), this.param_env) .sup(sup, sub) - }, + }), ) } @@ -841,11 +843,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( locations, || format!("eq_types({:?} = {:?})", a, b), - |this| { + CustomTypeOp::new(|this| { this.infcx .at(&ObligationCause::dummy(), this.param_env) .eq(b, a) - }, + }), ) } @@ -1635,12 +1637,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( location.at_self(), || format!("prove_predicates({:?})", predicates_vec), - |_this| { + CustomTypeOp::new(|_this| { Ok(InferOk { value: (), obligations, }) - }, + }), ).unwrap() } @@ -1683,7 +1685,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( location.to_locations(), || format!("normalize(value={:?})", value), - |this| { + CustomTypeOp::new(|this| { let Normalized { value, obligations } = this .infcx .at(&ObligationCause::dummy(), this.param_env) @@ -1697,7 +1699,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); }); Ok(InferOk { value, obligations }) - }, + }), ).unwrap() } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs new file mode 100644 index 0000000000000..b4c71344cfc8e --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -0,0 +1,43 @@ +// Copyright 2016 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 borrow_check::nll::type_check::TypeChecker; +use rustc::infer::InferResult; + +pub(super) trait TypeOp<'gcx, 'tcx> { + type Output; + + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; +} + +pub(super) struct CustomTypeOp { + closure: F +} + +impl CustomTypeOp +{ + pub(super) fn new<'gcx, 'tcx, R>(closure: F) -> Self + where + F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + { + CustomTypeOp { closure } + } +} + +impl<'gcx, 'tcx, F, R> TypeOp<'gcx, 'tcx> for CustomTypeOp +where + F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, +{ + type Output = R; + + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { + (self.closure)(type_checker) + } +} From 2b52793f74484c9cb69901d34627210ad3a5ba70 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:29:14 -0400 Subject: [PATCH 02/72] introduce `Subtype` type_op --- .../borrow_check/nll/type_check/mod.rs | 6 +--- .../borrow_check/nll/type_check/type_op.rs | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 5d06513e2a858..dd2af3f6f275d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -826,11 +826,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( locations, || format!("sub_types({:?} <: {:?})", sub, sup), - CustomTypeOp::new(|this| { - this.infcx - .at(&ObligationCause::dummy(), this.param_env) - .sup(sup, sub) - }), + type_op::Subtype::new(sub, sup), ) } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index b4c71344cfc8e..e31af735d5577 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -10,19 +10,23 @@ use borrow_check::nll::type_check::TypeChecker; use rustc::infer::InferResult; +use rustc::traits::ObligationCause; +use rustc::ty::Ty; pub(super) trait TypeOp<'gcx, 'tcx> { type Output; - fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; + fn perform( + self, + type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, + ) -> InferResult<'tcx, Self::Output>; } pub(super) struct CustomTypeOp { - closure: F + closure: F, } -impl CustomTypeOp -{ +impl CustomTypeOp { pub(super) fn new<'gcx, 'tcx, R>(closure: F) -> Self where F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, @@ -41,3 +45,25 @@ where (self.closure)(type_checker) } } + +pub(super) struct Subtype<'tcx> { + sub: Ty<'tcx>, + sup: Ty<'tcx>, +} + +impl<'tcx> Subtype<'tcx> { + pub(super) fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + Self { sub, sup } + } +} + +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { + type Output = (); + + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + type_checker.infcx + .at(&ObligationCause::dummy(), type_checker.param_env) + .sup(self.sup, self.sub) + } +} + From 9b4fe442809271b862e487e6af6cccad05771785 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:30:35 -0400 Subject: [PATCH 03/72] introduce `Eq` type-op --- .../borrow_check/nll/type_check/mod.rs | 6 +---- .../borrow_check/nll/type_check/type_op.rs | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index dd2af3f6f275d..9a51ec9cce92d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -839,11 +839,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( locations, || format!("eq_types({:?} = {:?})", a, b), - CustomTypeOp::new(|this| { - this.infcx - .at(&ObligationCause::dummy(), this.param_env) - .eq(b, a) - }), + type_op::Eq::new(b, a) ) } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index e31af735d5577..3598114ed8d6a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -67,3 +67,25 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } +pub(super) struct Eq<'tcx> { + a: Ty<'tcx>, + b: Ty<'tcx>, +} + +impl<'tcx> Eq<'tcx> { + pub(super) fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + Self { a, b } + } +} + +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { + type Output = (); + + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + type_checker.infcx + .at(&ObligationCause::dummy(), type_checker.param_env) + .eq(self.a, self.b) + } +} + + From 7c62461c39d5cf8899d419be77a0ec48dd9f14bc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:39:49 -0400 Subject: [PATCH 04/72] introduce `trivial_noop` to accommodate micro-optimizations --- .../borrow_check/nll/type_check/mod.rs | 14 +++-------- .../borrow_check/nll/type_check/type_op.rs | 25 +++++++++++++++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 9a51ec9cce92d..7a405f0a7093f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -736,6 +736,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { describe_op: impl Fn() -> String, op: impl TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { + if let Some(r) = op.trivial_noop() { + return Ok(r); + } + let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data( || format!("{} at {:?}", describe_op(), locations), op, @@ -818,11 +822,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { sup: Ty<'tcx>, locations: Locations, ) -> UnitResult<'tcx> { - // Micro-optimization. - if sub == sup { - return Ok(()); - } - self.fully_perform_op( locations, || format!("sub_types({:?} <: {:?})", sub, sup), @@ -831,11 +830,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { - // Micro-optimization. - if a == b { - return Ok(()); - } - self.fully_perform_op( locations, || format!("eq_types({:?} = {:?})", a, b), diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index 3598114ed8d6a..97453231273bf 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -16,6 +16,9 @@ use rustc::ty::Ty; pub(super) trait TypeOp<'gcx, 'tcx> { type Output; + /// Micro-optimization point: true if this is trivially true. + fn trivial_noop(&self) -> Option; + fn perform( self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, @@ -41,6 +44,10 @@ where { type Output = R; + fn trivial_noop(&self) -> Option { + None + } + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { (self.closure)(type_checker) } @@ -60,6 +67,14 @@ impl<'tcx> Subtype<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { type Output = (); + fn trivial_noop(&self) -> Option { + if self.sub == self.sup { + Some(()) + } else { + None + } + } + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { type_checker.infcx .at(&ObligationCause::dummy(), type_checker.param_env) @@ -81,11 +96,17 @@ impl<'tcx> Eq<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { type Output = (); + fn trivial_noop(&self) -> Option { + if self.a == self.b { + Some(()) + } else { + None + } + } + fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { type_checker.infcx .at(&ObligationCause::dummy(), type_checker.param_env) .eq(self.a, self.b) } } - - From 214d7650c91e777c445a8ef0733c8553e34b2658 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:47:28 -0400 Subject: [PATCH 05/72] introduce `prove_predicates` type op --- .../borrow_check/nll/type_check/mod.rs | 34 +++------- .../borrow_check/nll/type_check/type_op.rs | 62 ++++++++++++++++--- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 7a405f0a7093f..5e9b3ad5054c3 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -28,7 +28,7 @@ use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; use rustc::traits::query::NoSolution; -use rustc::traits::{self, ObligationCause, Normalized, TraitEngine}; +use rustc::traits::{ObligationCause, Normalized, TraitEngine}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; @@ -833,7 +833,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( locations, || format!("eq_types({:?} = {:?})", a, b), - type_op::Eq::new(b, a) + type_op::Eq::new(b, a), ) } @@ -1590,27 +1590,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); } - fn prove_predicates(&mut self, predicates: T, location: Location) - where - T: IntoIterator> + Clone, - { - let cause = ObligationCause::dummy(); - let obligations: Vec<_> = predicates - .into_iter() - .map(|p| traits::Obligation::new(cause.clone(), self.param_env, p)) - .collect(); - - // Micro-optimization - if obligations.is_empty() { - return; - } - + fn prove_predicates( + &mut self, + predicates: impl IntoIterator> + Clone, + location: Location, + ) { // This intermediate vector is mildly unfortunate, in that we // sometimes create it even when logging is disabled, but only // if debug-info is enabled, and I doubt it is actually // expensive. -nmatsakis let predicates_vec: Vec<_> = if cfg!(debug_assertions) { - obligations.iter().map(|o| o.predicate).collect() + predicates.clone().into_iter().collect() } else { Vec::new() }; @@ -1620,15 +1610,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { predicates_vec, location, ); + let param_env = self.param_env; self.fully_perform_op( location.at_self(), || format!("prove_predicates({:?})", predicates_vec), - CustomTypeOp::new(|_this| { - Ok(InferOk { - value: (), - obligations, - }) - }), + type_op::ProvePredicates::new(param_env, predicates), ).unwrap() } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index 97453231273bf..0b35f3501fc79 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -9,9 +9,9 @@ // except according to those terms. use borrow_check::nll::type_check::TypeChecker; -use rustc::infer::InferResult; -use rustc::traits::ObligationCause; -use rustc::ty::Ty; +use rustc::infer::{InferOk, InferResult}; +use rustc::traits::{Obligation, ObligationCause, PredicateObligation}; +use rustc::ty::{ParamEnv, Predicate, Ty}; pub(super) trait TypeOp<'gcx, 'tcx> { type Output; @@ -75,8 +75,12 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } - fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - type_checker.infcx + fn perform( + self, + type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, + ) -> InferResult<'tcx, Self::Output> { + type_checker + .infcx .at(&ObligationCause::dummy(), type_checker.param_env) .sup(self.sup, self.sub) } @@ -104,9 +108,53 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { } } - fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - type_checker.infcx + fn perform( + self, + type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, + ) -> InferResult<'tcx, Self::Output> { + type_checker + .infcx .at(&ObligationCause::dummy(), type_checker.param_env) .eq(self.a, self.b) } } + +pub(super) struct ProvePredicates<'tcx> { + obligations: Vec>, +} + +impl<'tcx> ProvePredicates<'tcx> { + pub(super) fn new( + param_env: ParamEnv<'tcx>, + predicates: impl IntoIterator>, + ) -> Self { + ProvePredicates { + obligations: predicates + .into_iter() + .map(|p| Obligation::new(ObligationCause::dummy(), param_env, p)) + .collect(), + } + } +} + +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { + type Output = (); + + fn trivial_noop(&self) -> Option { + if self.obligations.is_empty() { + Some(()) + } else { + None + } + } + + fn perform( + self, + _type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, + ) -> InferResult<'tcx, Self::Output> { + Ok(InferOk { + value: (), + obligations: self.obligations, + }) + } +} From 8147d17d8e3ac5c5dc9562862050f0876c556e88 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:52:26 -0400 Subject: [PATCH 06/72] make `normalize` take ownership of the thing to be normalized --- .../nll/type_check/input_output.rs | 4 ++-- .../borrow_check/nll/type_check/mod.rs | 18 +++++++++--------- .../borrow_check/nll/type_check/type_op.rs | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index be0a2494b04fc..708ff4aa2ba8e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -51,7 +51,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // Equate expected input tys with those in the MIR. let argument_locals = (1..).map(Local::new); for (&unnormalized_input_ty, local) in unnormalized_input_tys.iter().zip(argument_locals) { - let input_ty = self.normalize(&unnormalized_input_ty, Locations::All); + let input_ty = self.normalize(unnormalized_input_ty, Locations::All); let mir_input_ty = mir.local_decls[local].ty; self.equate_normalized_input_or_output(input_ty, mir_input_ty); } @@ -71,7 +71,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { "equate_inputs_and_outputs: unnormalized_output_ty={:?}", unnormalized_output_ty ); - let output_ty = self.normalize(&unnormalized_output_ty, Locations::All); + let output_ty = self.normalize(unnormalized_output_ty, Locations::All); debug!( "equate_inputs_and_outputs: normalized output_ty={:?}", output_ty diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 5e9b3ad5054c3..d9e3ecb8f8537 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -289,7 +289,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); let predicates = - type_checker.normalize(&instantiated_predicates.predicates, location); + type_checker.normalize(instantiated_predicates.predicates, location); type_checker.prove_predicates(predicates, location); } @@ -346,7 +346,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { Place::Static(box Static { def_id, ty: sty }) => { let sty = self.sanitize_type(place, sty); let ty = self.tcx().type_of(def_id); - let ty = self.cx.normalize(&ty, location); + let ty = self.cx.normalize(ty, location); if let Err(terr) = self.cx.eq_types(ty, sty, location.at_self()) { span_mirbug!( self, @@ -1023,7 +1023,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { LateBoundRegionConversionTime::FnCall, &sig, ); - let sig = self.normalize(&sig, term_location); + let sig = self.normalize(sig, term_location); self.check_call_dest(mir, term, &sig, destination, term_location); self.prove_predicates( @@ -1311,7 +1311,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let variant = &def.variants[variant_index]; let adj_field_index = active_field_index.unwrap_or(field_index); if let Some(field) = variant.fields.get(adj_field_index) { - Ok(self.normalize(&field.ty(tcx, substs), location)) + Ok(self.normalize(field.ty(tcx, substs), location)) } else { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len(), @@ -1385,7 +1385,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // function definition. When we extract the // signature, it comes from the `fn_sig` query, // and hence may contain unnormalized results. - let fn_sig = self.normalize(&fn_sig, location); + let fn_sig = self.normalize(fn_sig, location); let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); @@ -1430,7 +1430,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // function definition. When we extract the // signature, it comes from the `fn_sig` query, // and hence may contain unnormalized results. - let fn_sig = self.normalize(&fn_sig, location); + let fn_sig = self.normalize(fn_sig, location); let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); @@ -1576,7 +1576,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), }; - let predicates = self.normalize(&instantiated_predicates.predicates, location); + let predicates = self.normalize(instantiated_predicates.predicates, location); debug!("prove_aggregate_predicates: predicates={:?}", predicates); self.prove_predicates(predicates, location); } @@ -1644,7 +1644,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn normalize(&mut self, value: &T, location: impl ToLocations) -> T + fn normalize(&mut self, value: T, location: impl ToLocations) -> T where T: fmt::Debug + TypeFoldable<'tcx>, { @@ -1661,7 +1661,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let Normalized { value, obligations } = this .infcx .at(&ObligationCause::dummy(), this.param_env) - .normalize(value) + .normalize(&value) .unwrap_or_else(|NoSolution| { span_bug!( this.last_span, diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index 0b35f3501fc79..602abbdd4e7a4 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -19,6 +19,7 @@ pub(super) trait TypeOp<'gcx, 'tcx> { /// Micro-optimization point: true if this is trivially true. fn trivial_noop(&self) -> Option; + /// Produce a description of the operation for the debug logs. fn perform( self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, From f998628e5c661f07bf0db7985b2949411a23064c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 09:57:13 -0400 Subject: [PATCH 07/72] let `trivial_noop` take ownership of `self` --- .../borrow_check/nll/type_check/mod.rs | 25 ++++++++-------- .../borrow_check/nll/type_check/type_op.rs | 29 ++++++++++--------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d9e3ecb8f8537..e6211c4ed7fc4 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -736,20 +736,21 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { describe_op: impl Fn() -> String, op: impl TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { - if let Some(r) = op.trivial_noop() { - return Ok(r); - } - - let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data( - || format!("{} at {:?}", describe_op(), locations), - op, - )?; + match op.trivial_noop() { + Ok(r) => Ok(r), + Err(op) => { + let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data( + || format!("{} at {:?}", describe_op(), locations), + op, + )?; + + if let Some(data) = opt_data { + self.push_region_constraints(locations, data); + } - if let Some(data) = opt_data { - self.push_region_constraints(locations, data); + Ok(r) + } } - - Ok(r) } fn push_region_constraints( diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index 602abbdd4e7a4..d85a83ac3fc53 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -13,11 +13,12 @@ use rustc::infer::{InferOk, InferResult}; use rustc::traits::{Obligation, ObligationCause, PredicateObligation}; use rustc::ty::{ParamEnv, Predicate, Ty}; -pub(super) trait TypeOp<'gcx, 'tcx> { +pub(super) trait TypeOp<'gcx, 'tcx>: Sized { type Output; - /// Micro-optimization point: true if this is trivially true. - fn trivial_noop(&self) -> Option; + /// Micro-optimization: returns `Ok(x)` if we can trivially + /// produce the output, else returns `Err(self)` back. + fn trivial_noop(self) -> Result; /// Produce a description of the operation for the debug logs. fn perform( @@ -45,8 +46,8 @@ where { type Output = R; - fn trivial_noop(&self) -> Option { - None + fn trivial_noop(self) -> Result { + Err(self) } fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { @@ -68,11 +69,11 @@ impl<'tcx> Subtype<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { type Output = (); - fn trivial_noop(&self) -> Option { + fn trivial_noop(self) -> Result { if self.sub == self.sup { - Some(()) + Ok(()) } else { - None + Err(self) } } @@ -101,11 +102,11 @@ impl<'tcx> Eq<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { type Output = (); - fn trivial_noop(&self) -> Option { + fn trivial_noop(self) -> Result { if self.a == self.b { - Some(()) + Ok(()) } else { - None + Err(self) } } @@ -141,11 +142,11 @@ impl<'tcx> ProvePredicates<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { type Output = (); - fn trivial_noop(&self) -> Option { + fn trivial_noop(self) -> Result { if self.obligations.is_empty() { - Some(()) + Ok(()) } else { - None + Err(self) } } From 846cc263cf4aee40064598c8a9c1d26c50daa7c2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Jun 2018 10:33:07 -0400 Subject: [PATCH 08/72] make normalize into an op --- .../borrow_check/nll/type_check/mod.rs | 32 +++-------- .../borrow_check/nll/type_check/type_op.rs | 53 ++++++++++++++++++- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index e6211c4ed7fc4..99d8ed85af3a8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -15,7 +15,6 @@ use borrow_check::location::LocationTable; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::Cause; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, OutlivesConstraint, TypeTest}; -use borrow_check::nll::type_check::type_op::{CustomTypeOp, TypeOp}; use borrow_check::nll::universal_regions::UniversalRegions; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; @@ -27,8 +26,7 @@ use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; -use rustc::traits::query::NoSolution; -use rustc::traits::{ObligationCause, Normalized, TraitEngine}; +use rustc::traits::{ObligationCause, TraitEngine}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; @@ -734,7 +732,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { &mut self, locations: Locations, describe_op: impl Fn() -> String, - op: impl TypeOp<'gcx, 'tcx, Output = R>, + op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { match op.trivial_noop() { Ok(r) => Ok(r), @@ -784,7 +782,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn fully_perform_op_and_get_region_constraint_data( &mut self, describe_op: impl Fn() -> String, - op: impl TypeOp<'gcx, 'tcx, Output = R>, + op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, ) -> Result<(R, Option>>), TypeError<'tcx>> { if cfg!(debug_assertions) { info!( @@ -1649,30 +1647,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { where T: fmt::Debug + TypeFoldable<'tcx>, { - // Micro-optimization: avoid work when we don't have to - if !value.has_projections() { - return value.clone(); - } - debug!("normalize(value={:?}, location={:?})", value, location); + let value1 = value.clone(); // FIXME move describe into type_op self.fully_perform_op( location.to_locations(), - || format!("normalize(value={:?})", value), - CustomTypeOp::new(|this| { - let Normalized { value, obligations } = this - .infcx - .at(&ObligationCause::dummy(), this.param_env) - .normalize(&value) - .unwrap_or_else(|NoSolution| { - span_bug!( - this.last_span, - "normalization of `{:?}` failed at {:?}", - value, - location, - ); - }); - Ok(InferOk { value, obligations }) - }), + || format!("normalize(value={:?})", value1), + type_op::Normalize::new(value), ).unwrap() } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index d85a83ac3fc53..43c11c61eff61 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -10,8 +10,11 @@ use borrow_check::nll::type_check::TypeChecker; use rustc::infer::{InferOk, InferResult}; -use rustc::traits::{Obligation, ObligationCause, PredicateObligation}; +use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation}; +use rustc::traits::query::NoSolution; use rustc::ty::{ParamEnv, Predicate, Ty}; +use rustc::ty::fold::TypeFoldable; +use std::fmt; pub(super) trait TypeOp<'gcx, 'tcx>: Sized { type Output; @@ -160,3 +163,51 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { }) } } + +pub(super) struct Normalize { + value: T +} + +impl<'tcx, T> Normalize +where + T: fmt::Debug + TypeFoldable<'tcx>, +{ + pub(super) fn new( + value: T + ) -> Self { + Self { value } + } +} + +impl<'gcx, 'tcx, T> TypeOp<'gcx, 'tcx> for Normalize +where + T: fmt::Debug + TypeFoldable<'tcx>, +{ + type Output = T; + + fn trivial_noop(self) -> Result { + if !self.value.has_projections() { + Ok(self.value) + } else { + Err(self) + } + } + + fn perform( + self, + type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, + ) -> InferResult<'tcx, Self::Output> { + let Normalized { value, obligations } = type_checker + .infcx + .at(&ObligationCause::dummy(), type_checker.param_env) + .normalize(&self.value) + .unwrap_or_else(|NoSolution| { + span_bug!( + type_checker.last_span, + "normalization of `{:?}` failed", + self.value, + ); + }); + Ok(InferOk { value, obligations }) + } +} From 13e77934e52a4d17f765412df391fadab8a839a2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 04:35:02 -0400 Subject: [PATCH 09/72] create `InfcxTypeOp` that only depend on an `infcx` We want any add'l context required to be passed through the struct itself. --- .../borrow_check/nll/type_check/mod.rs | 9 +- .../borrow_check/nll/type_check/type_op.rs | 108 ++++++++++-------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 99d8ed85af3a8..739f1eeb05f91 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -821,18 +821,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { sup: Ty<'tcx>, locations: Locations, ) -> UnitResult<'tcx> { + let param_env = self.param_env; self.fully_perform_op( locations, || format!("sub_types({:?} <: {:?})", sub, sup), - type_op::Subtype::new(sub, sup), + type_op::Subtype::new(param_env, sub, sup), ) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { + let param_env = self.param_env; self.fully_perform_op( locations, || format!("eq_types({:?} = {:?})", a, b), - type_op::Eq::new(b, a), + type_op::Eq::new(param_env, b, a), ) } @@ -1648,11 +1650,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { T: fmt::Debug + TypeFoldable<'tcx>, { debug!("normalize(value={:?}, location={:?})", value, location); + let param_env = self.param_env; let value1 = value.clone(); // FIXME move describe into type_op self.fully_perform_op( location.to_locations(), || format!("normalize(value={:?})", value1), - type_op::Normalize::new(value), + type_op::Normalize::new(param_env, value), ).unwrap() } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index 43c11c61eff61..b39ae0ed4ee74 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -9,11 +9,11 @@ // except according to those terms. use borrow_check::nll::type_check::TypeChecker; -use rustc::infer::{InferOk, InferResult}; -use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation}; +use rustc::infer::{InferCtxt, InferOk, InferResult}; use rustc::traits::query::NoSolution; -use rustc::ty::{ParamEnv, Predicate, Ty}; +use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation}; use rustc::ty::fold::TypeFoldable; +use rustc::ty::{ParamEnv, Predicate, Ty}; use std::fmt; pub(super) trait TypeOp<'gcx, 'tcx>: Sized { @@ -58,18 +58,52 @@ where } } +pub(super) trait InfcxTypeOp<'gcx, 'tcx>: Sized { + type Output; + + /// Micro-optimization: returns `Ok(x)` if we can trivially + /// produce the output, else returns `Err(self)` back. + fn trivial_noop(self) -> Result; + + /// Produce a description of the operation for the debug logs. + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; +} + +impl<'gcx, 'tcx, OP> TypeOp<'gcx, 'tcx> for OP +where + OP: InfcxTypeOp<'gcx, 'tcx>, +{ + type Output = OP::Output; + + fn trivial_noop(self) -> Result { + InfcxTypeOp::trivial_noop(self) + } + + fn perform( + self, + type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, + ) -> InferResult<'tcx, OP::Output> { + InfcxTypeOp::perform(self, type_checker.infcx) + } +} + pub(super) struct Subtype<'tcx> { + param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>, } impl<'tcx> Subtype<'tcx> { - pub(super) fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { - Self { sub, sup } + pub(super) fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + Self { + param_env, + sub, + sup, + } } } -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { +impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for Subtype<'tcx> { type Output = (); fn trivial_noop(self) -> Result { @@ -80,29 +114,26 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } - fn perform( - self, - type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, - ) -> InferResult<'tcx, Self::Output> { - type_checker - .infcx - .at(&ObligationCause::dummy(), type_checker.param_env) + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + infcx + .at(&ObligationCause::dummy(), self.param_env) .sup(self.sup, self.sub) } } pub(super) struct Eq<'tcx> { + param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, } impl<'tcx> Eq<'tcx> { - pub(super) fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { - Self { a, b } + pub(super) fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + Self { param_env, a, b } } } -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { +impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for Eq<'tcx> { type Output = (); fn trivial_noop(self) -> Result { @@ -113,13 +144,9 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { } } - fn perform( - self, - type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, - ) -> InferResult<'tcx, Self::Output> { - type_checker - .infcx - .at(&ObligationCause::dummy(), type_checker.param_env) + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + infcx + .at(&ObligationCause::dummy(), self.param_env) .eq(self.a, self.b) } } @@ -142,7 +169,7 @@ impl<'tcx> ProvePredicates<'tcx> { } } -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { +impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { type Output = (); fn trivial_noop(self) -> Result { @@ -153,10 +180,7 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { } } - fn perform( - self, - _type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, - ) -> InferResult<'tcx, Self::Output> { + fn perform(self, _infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { Ok(InferOk { value: (), obligations: self.obligations, @@ -164,22 +188,21 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { } } -pub(super) struct Normalize { - value: T +pub(super) struct Normalize<'tcx, T> { + param_env: ParamEnv<'tcx>, + value: T, } -impl<'tcx, T> Normalize +impl<'tcx, T> Normalize<'tcx, T> where T: fmt::Debug + TypeFoldable<'tcx>, { - pub(super) fn new( - value: T - ) -> Self { - Self { value } + pub(super) fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { + Self { param_env, value } } } -impl<'gcx, 'tcx, T> TypeOp<'gcx, 'tcx> for Normalize +impl<'gcx, 'tcx, T> InfcxTypeOp<'gcx, 'tcx> for Normalize<'tcx, T> where T: fmt::Debug + TypeFoldable<'tcx>, { @@ -193,17 +216,12 @@ where } } - fn perform( - self, - type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, - ) -> InferResult<'tcx, Self::Output> { - let Normalized { value, obligations } = type_checker - .infcx - .at(&ObligationCause::dummy(), type_checker.param_env) + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + let Normalized { value, obligations } = infcx + .at(&ObligationCause::dummy(), self.param_env) .normalize(&self.value) .unwrap_or_else(|NoSolution| { - span_bug!( - type_checker.last_span, + bug!( "normalization of `{:?}` failed", self.value, ); From 5829746dddf4f1e3d498a630fc475eaec6ac753d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 04:35:53 -0400 Subject: [PATCH 10/72] resolve type vars *before* entering `type_must_outlive` All recursive calls only use type-vars that should already be resolved, because they were obtained from `ty`. --- src/librustc/infer/outlives/obligations.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index e5461685bd470..f2eb38abe4871 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -186,6 +186,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { || infer::RelateParamBound(cause.span, sup_type), ); + let sup_type = self.resolve_type_vars_if_possible(&sup_type); outlives.type_must_outlive(origin, sup_type, sub_region); } } @@ -203,6 +204,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) { let outlives = TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + let ty = self.resolve_type_vars_if_possible(&ty); outlives.type_must_outlive(origin, ty, region); } } @@ -246,8 +248,6 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { ty: Ty<'tcx>, region: ty::Region<'tcx>, ) { - let ty = self.infcx.resolve_type_vars_if_possible(&ty); - debug!( "type_must_outlive(ty={:?}, region={:?}, origin={:?})", ty, From b858ed5919f689c0451609fa8bed62d4ee2e1ef1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 04:56:54 -0400 Subject: [PATCH 11/72] obligations.rs: rustfmt --- src/librustc/infer/outlives/obligations.rs | 47 ++++++++-------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index f2eb38abe4871..55f0b430dae45 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -71,11 +71,11 @@ use hir::def_id::DefId; use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use syntax::ast; use traits; -use ty::{self, Ty, TyCtxt, TypeFoldable}; -use ty::subst::{Subst, Substs}; use ty::outlives::Component; -use syntax::ast; +use ty::subst::{Subst, Substs}; +use ty::{self, Ty, TyCtxt, TypeFoldable}; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Registers that the given region obligation must be resolved @@ -90,8 +90,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) { debug!( "register_region_obligation(body_id={:?}, obligation={:?})", - body_id, - obligation + body_id, obligation ); self.region_obligations @@ -100,13 +99,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } /// Trait queries just want to pass back type obligations "as is" - pub fn take_registered_region_obligations( - &self, - ) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> { - ::std::mem::replace( - &mut *self.region_obligations.borrow_mut(), - vec![], - ) + pub fn take_registered_region_obligations(&self) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> { + ::std::mem::replace(&mut *self.region_obligations.borrow_mut(), vec![]) } /// Process the region obligations that must be proven (during @@ -176,15 +170,12 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { { debug!( "process_registered_region_obligations: sup_type={:?} sub_region={:?} cause={:?}", - sup_type, - sub_region, - cause + sup_type, sub_region, cause ); - let origin = SubregionOrigin::from_obligation_cause( - &cause, - || infer::RelateParamBound(cause.span, sup_type), - ); + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateParamBound(cause.span, sup_type) + }); let sup_type = self.resolve_type_vars_if_possible(&sup_type); outlives.type_must_outlive(origin, sup_type, sub_region); @@ -250,9 +241,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { ) { debug!( "type_must_outlive(ty={:?}, region={:?}, origin={:?})", - ty, - region, - origin + ty, region, origin ); assert!(!ty.has_escaping_regions()); @@ -307,9 +296,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { ) { debug!( "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", - region, - param_ty, - origin + region, param_ty, origin ); let verify_bound = self.param_bound(param_ty); @@ -326,9 +313,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { ) { debug!( "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", - region, - projection_ty, - origin + region, projection_ty, origin ); // This case is thorny for inference. The fundamental problem is @@ -469,12 +454,12 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { ) -> VerifyBound<'tcx> { debug!( "projection_bound(declared_bounds={:?}, projection_ty={:?})", - declared_bounds, - projection_ty + declared_bounds, projection_ty ); // see the extensive comment in projection_must_outlive - let ty = self.infcx + let ty = self + .infcx .tcx .mk_projection(projection_ty.item_def_id, projection_ty.substs); let recursive_bound = self.recursive_type_bound(ty); From 65ceec71ecf4d87e16eb8a273c7d7cde0383b772 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 04:57:18 -0400 Subject: [PATCH 12/72] make `TypeOutlives` parameterized over a delegate It was only using the `infcx` to "accumulate" constraints anyhow. --- src/librustc/infer/outlives/obligations.rs | 121 +++++++++++++++------ 1 file changed, 90 insertions(+), 31 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 55f0b430dae45..c0d530618eca1 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -159,8 +159,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } } - let outlives = - TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + let outlives = &mut TypeOutlives::new( + self, + self.tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ); for RegionObligation { sup_type, @@ -193,32 +198,68 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ty: Ty<'tcx>, region: ty::Region<'tcx>, ) { - let outlives = - TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + let outlives = &mut TypeOutlives::new( + self, + self.tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ); let ty = self.resolve_type_vars_if_possible(&ty); outlives.type_must_outlive(origin, ty, region); } } -#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =) -struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { +/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` +/// obligation into a series of `'a: 'b` constraints and "verifys", as +/// described on the module comment. The final constraints are emitted +/// via a "delegate" of type `D` -- this is usually the `infcx`, which +/// accrues them into the `region_obligations` code, but for NLL we +/// use something else. +pub struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx, D> +where + D: TypeOutlivesDelegate<'tcx>, +{ // See the comments on `process_registered_region_obligations` for the meaning // of these fields. - infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + delegate: D, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, } -impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { +pub trait TypeOutlivesDelegate<'tcx> { + fn push_sub_region_constraint( + &mut self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ); + + fn push_verify( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ); +} + +impl<'cx, 'gcx, 'tcx, D> TypeOutlives<'cx, 'gcx, 'tcx, D> +where + D: TypeOutlivesDelegate<'tcx>, +{ fn new( - infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + delegate: D, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, ) -> Self { Self { - infcx, + delegate, + tcx, region_bound_pairs, implicit_region_bound, param_env, @@ -234,7 +275,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { /// - `ty`, the type `T` /// - `region`, the region `'a` fn type_must_outlive( - &self, + &mut self, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, region: ty::Region<'tcx>, @@ -246,16 +287,12 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { assert!(!ty.has_escaping_regions()); - let components = self.tcx().outlives_components(ty); + let components = self.tcx.outlives_components(ty); self.components_must_outlive(origin, components, region); } - fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { - self.infcx.tcx - } - fn components_must_outlive( - &self, + &mut self, origin: infer::SubregionOrigin<'tcx>, components: Vec>, region: ty::Region<'tcx>, @@ -264,7 +301,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { let origin = origin.clone(); match component { Component::Region(region1) => { - self.infcx.sub_regions(origin, region, region1); + self.delegate.push_sub_region_constraint(origin, region, region1); } Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, param_ty); @@ -279,7 +316,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // ignore this, we presume it will yield an error // later, since if a type variable is not resolved by // this point it never will be - self.infcx.tcx.sess.delay_span_bug( + self.tcx.sess.delay_span_bug( origin.span(), &format!("unresolved inference variable in outlives: {:?}", v), ); @@ -289,7 +326,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } fn param_ty_must_outlive( - &self, + &mut self, origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, param_ty: ty::ParamTy, @@ -301,12 +338,12 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { let verify_bound = self.param_bound(param_ty); let generic = GenericKind::Param(param_ty); - self.infcx - .verify_generic_bound(origin, generic, region, verify_bound); + self.delegate + .push_verify(origin, generic, region, verify_bound); } fn projection_must_outlive( - &self, + &mut self, origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, @@ -367,7 +404,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } for r in projection_ty.substs.regions() { - self.infcx.sub_regions(origin.clone(), region, r); + self.delegate.push_sub_region_constraint(origin.clone(), region, r); } return; @@ -393,7 +430,8 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { .any(|r| env_bounds.contains(&r)) { debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.infcx.sub_regions(origin.clone(), region, unique_bound); + self.delegate + .push_sub_region_constraint(origin.clone(), region, unique_bound); return; } } @@ -405,8 +443,8 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // even though a satisfactory solution exists. let verify_bound = self.projection_bound(env_bounds, projection_ty); let generic = GenericKind::Projection(projection_ty); - self.infcx - .verify_generic_bound(origin, generic.clone(), region, verify_bound); + self.delegate + .push_verify(origin, generic.clone(), region, verify_bound); } fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { @@ -459,7 +497,6 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // see the extensive comment in projection_must_outlive let ty = self - .infcx .tcx .mk_projection(projection_ty.item_def_id, projection_ty.substs); let recursive_bound = self.recursive_type_bound(ty); @@ -492,7 +529,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { &self, generic: GenericKind<'tcx>, ) -> Vec> { - let tcx = self.tcx(); + let tcx = self.tcx; // To start, collect bounds from user environment. Note that // parameter environments are already elaborated, so we don't @@ -544,7 +581,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { debug!("projection_bounds(projection_ty={:?})", projection_ty); let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); for r in &mut bounds { - *r = r.subst(self.tcx(), projection_ty.substs); + *r = r.subst(self.tcx, projection_ty.substs); } bounds } @@ -583,7 +620,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { &self, assoc_item_def_id: DefId, ) -> Vec> { - let tcx = self.tcx(); + let tcx = self.tcx; let assoc_item = tcx.associated_item(assoc_item_def_id); let trait_def_id = assoc_item.container.assert_trait(); let trait_predicates = tcx.predicates_of(trait_def_id); @@ -619,3 +656,25 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { .collect() } } + +impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, 'tcx> { + fn push_sub_region_constraint( + &mut self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) { + self.sub_regions(origin, a, b) + } + + fn push_verify( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + self.verify_generic_bound(origin, kind, a, bound) + } +} + From e7a9e7aef2f90dd3e166be0c42570a70890d5dce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 05:14:32 -0400 Subject: [PATCH 13/72] extract a `enabled` helper to remove some ad-hoc conditionals --- src/librustc_mir/borrow_check/nll/facts.rs | 10 ++++++++++ src/librustc_mir/borrow_check/nll/mod.rs | 4 +--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/facts.rs b/src/librustc_mir/borrow_check/nll/facts.rs index 6cb8e64b9f5b4..fe2fc7e37b761 100644 --- a/src/librustc_mir/borrow_check/nll/facts.rs +++ b/src/librustc_mir/borrow_check/nll/facts.rs @@ -23,6 +23,10 @@ use std::path::Path; crate type AllFacts = PoloniusAllFacts; crate trait AllFactsExt { + /// Returns true if there is a need to gather `AllFacts` given the + /// current `-Z` flags. + fn enabled(tcx: TyCtxt<'_, '_, '_>) -> bool; + fn write_to_dir( &self, dir: impl AsRef, @@ -31,6 +35,12 @@ crate trait AllFactsExt { } impl AllFactsExt for AllFacts { + /// Return + fn enabled(tcx: TyCtxt<'_, '_, '_>) -> bool { + tcx.sess.opts.debugging_opts.nll_facts + || tcx.sess.opts.debugging_opts.polonius + } + fn write_to_dir( &self, dir: impl AsRef, diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index dcb52a3b18a72..e26665e8291bf 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -91,9 +91,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( Option>>, Option>, ) { - let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts - || infcx.tcx.sess.opts.debugging_opts.polonius - { + let mut all_facts = if AllFacts::enabled(infcx.tcx) { Some(AllFacts::default()) } else { None From 7a641cb14525d1111d24e9060ebe07c3ce10ab83 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 05:29:25 -0400 Subject: [PATCH 14/72] make `TypeOp` implement debug instead of carrying a closure This allows us to avoid some silly clones etc. --- src/librustc_mir/borrow_check/nll/facts.rs | 2 +- .../nll/type_check/input_output.rs | 119 +++++++++--------- .../borrow_check/nll/type_check/liveness.rs | 15 ++- .../borrow_check/nll/type_check/mod.rs | 27 +--- .../borrow_check/nll/type_check/type_op.rs | 35 ++++-- 5 files changed, 98 insertions(+), 100 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/facts.rs b/src/librustc_mir/borrow_check/nll/facts.rs index fe2fc7e37b761..2523711f936e1 100644 --- a/src/librustc_mir/borrow_check/nll/facts.rs +++ b/src/librustc_mir/borrow_check/nll/facts.rs @@ -12,7 +12,7 @@ use borrow_check::location::{LocationIndex, LocationTable}; use dataflow::indexes::BorrowIndex; use polonius_engine::AllFacts as PoloniusAllFacts; use polonius_engine::Atom; -use rustc::ty::RegionVid; +use rustc::ty::{RegionVid, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; use std::error::Error; use std::fmt::Debug; diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index 708ff4aa2ba8e..afc21948e4025 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -80,63 +80,66 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let anon_type_map = self.fully_perform_op( Locations::All, - || format!("input_output"), - CustomTypeOp::new(|cx| { - let mut obligations = ObligationAccumulator::default(); - - let dummy_body_id = ObligationCause::dummy().body_id; - let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types( - mir_def_id, - dummy_body_id, - cx.param_env, - &output_ty, - )); - debug!( - "equate_inputs_and_outputs: instantiated output_ty={:?}", - output_ty - ); - debug!( - "equate_inputs_and_outputs: anon_type_map={:#?}", - anon_type_map - ); - - debug!( - "equate_inputs_and_outputs: mir_output_ty={:?}", - mir_output_ty - ); - obligations.add( - infcx - .at(&ObligationCause::dummy(), cx.param_env) - .eq(output_ty, mir_output_ty)?, - ); - - for (&anon_def_id, anon_decl) in &anon_type_map { - let anon_defn_ty = tcx.type_of(anon_def_id); - let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); - let anon_defn_ty = renumber::renumber_regions( - cx.infcx, - TyContext::Location(Location::START), - &anon_defn_ty, + CustomTypeOp::new( + |cx| { + let mut obligations = ObligationAccumulator::default(); + + let dummy_body_id = ObligationCause::dummy().body_id; + let (output_ty, anon_type_map) = + obligations.add(infcx.instantiate_anon_types( + mir_def_id, + dummy_body_id, + cx.param_env, + &output_ty, + )); + debug!( + "equate_inputs_and_outputs: instantiated output_ty={:?}", + output_ty ); debug!( - "equate_inputs_and_outputs: concrete_ty={:?}", - anon_decl.concrete_ty + "equate_inputs_and_outputs: anon_type_map={:#?}", + anon_type_map + ); + + debug!( + "equate_inputs_and_outputs: mir_output_ty={:?}", + mir_output_ty ); - debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); obligations.add( infcx .at(&ObligationCause::dummy(), cx.param_env) - .eq(anon_decl.concrete_ty, anon_defn_ty)?, + .eq(output_ty, mir_output_ty)?, ); - } - - debug!("equate_inputs_and_outputs: equated"); - Ok(InferOk { - value: Some(anon_type_map), - obligations: obligations.into_vec(), - }) - }), + for (&anon_def_id, anon_decl) in &anon_type_map { + let anon_defn_ty = tcx.type_of(anon_def_id); + let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); + let anon_defn_ty = renumber::renumber_regions( + cx.infcx, + TyContext::Location(Location::START), + &anon_defn_ty, + ); + debug!( + "equate_inputs_and_outputs: concrete_ty={:?}", + anon_decl.concrete_ty + ); + debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); + obligations.add( + infcx + .at(&ObligationCause::dummy(), cx.param_env) + .eq(anon_decl.concrete_ty, anon_defn_ty)?, + ); + } + + debug!("equate_inputs_and_outputs: equated"); + + Ok(InferOk { + value: Some(anon_type_map), + obligations: obligations.into_vec(), + }) + }, + || format!("input_output"), + ), ).unwrap_or_else(|terr| { span_mirbug!( self, @@ -156,14 +159,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(anon_type_map) = anon_type_map { self.fully_perform_op( Locations::All, - || format!("anon_type_map"), - CustomTypeOp::new(|_cx| { - infcx.constrain_anon_types(&anon_type_map, universal_regions); - Ok(InferOk { - value: (), - obligations: vec![], - }) - }), + CustomTypeOp::new( + |_cx| { + infcx.constrain_anon_types(&anon_type_map, universal_regions); + Ok(InferOk { + value: (), + obligations: vec![], + }) + }, + || format!("anon_type_map"), + ), ).unwrap(); } } 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 d58dc1a601a7f..a032b2a9caabf 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -9,8 +9,8 @@ // except according to those terms. use borrow_check::nll::region_infer::Cause; -use borrow_check::nll::type_check::AtLocation; use borrow_check::nll::type_check::type_op::CustomTypeOp; +use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; @@ -171,8 +171,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo ); cx.tcx().for_each_free_region(&value, |live_region| { - cx - .constraints + cx.constraints .liveness_set .push((live_region, location, cause.clone())); }); @@ -219,15 +218,15 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); let (dropped_kinds, region_constraint_data) = - cx.fully_perform_op_and_get_region_constraint_data( - || format!("compute_drop_data(dropped_ty={:?})", dropped_ty), - CustomTypeOp::new(|cx| { + cx.fully_perform_op_and_get_region_constraint_data(CustomTypeOp::new( + |cx| { Ok(cx .infcx .at(&ObligationCause::dummy(), cx.param_env) .dropck_outlives(dropped_ty)) - }), - ).unwrap(); + }, + || format!("compute_drop_data(dropped_ty={:?})", dropped_ty), + )).unwrap(); DropData { dropped_kinds, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 739f1eeb05f91..959532fff8da5 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -731,16 +731,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn fully_perform_op( &mut self, locations: Locations, - describe_op: impl Fn() -> String, op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { match op.trivial_noop() { Ok(r) => Ok(r), Err(op) => { - let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data( - || format!("{} at {:?}", describe_op(), locations), - op, - )?; + let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data(op)?; if let Some(data) = opt_data { self.push_region_constraints(locations, data); @@ -781,14 +777,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { /// be generated there, so this can be useful for caching. fn fully_perform_op_and_get_region_constraint_data( &mut self, - describe_op: impl Fn() -> String, op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, ) -> Result<(R, Option>>), TypeError<'tcx>> { if cfg!(debug_assertions) { - info!( - "fully_perform_op_and_get_region_constraint_data({})", - describe_op(), - ); + info!("fully_perform_op_and_get_region_constraint_data({:?})", op,); } let mut fulfill_cx = TraitEngine::new(self.infcx.tcx); @@ -822,20 +814,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { locations: Locations, ) -> UnitResult<'tcx> { let param_env = self.param_env; - self.fully_perform_op( - locations, - || format!("sub_types({:?} <: {:?})", sub, sup), - type_op::Subtype::new(param_env, sub, sup), - ) + self.fully_perform_op(locations, type_op::Subtype::new(param_env, sub, sup)) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { let param_env = self.param_env; - self.fully_perform_op( - locations, - || format!("eq_types({:?} = {:?})", a, b), - type_op::Eq::new(param_env, b, a), - ) + self.fully_perform_op(locations, type_op::Eq::new(param_env, b, a)) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { @@ -1614,7 +1598,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.fully_perform_op( location.at_self(), - || format!("prove_predicates({:?})", predicates_vec), type_op::ProvePredicates::new(param_env, predicates), ).unwrap() } @@ -1651,10 +1634,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { { debug!("normalize(value={:?}, location={:?})", value, location); let param_env = self.param_env; - let value1 = value.clone(); // FIXME move describe into type_op self.fully_perform_op( location.to_locations(), - || format!("normalize(value={:?})", value1), type_op::Normalize::new(param_env, value), ).unwrap() } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index b39ae0ed4ee74..ff008d39d49b2 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -16,7 +16,7 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::{ParamEnv, Predicate, Ty}; use std::fmt; -pub(super) trait TypeOp<'gcx, 'tcx>: Sized { +pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; /// Micro-optimization: returns `Ok(x)` if we can trivially @@ -30,22 +30,25 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized { ) -> InferResult<'tcx, Self::Output>; } -pub(super) struct CustomTypeOp { +pub(super) struct CustomTypeOp { closure: F, + description: G, } -impl CustomTypeOp { - pub(super) fn new<'gcx, 'tcx, R>(closure: F) -> Self +impl CustomTypeOp { + pub(super) fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self where F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + G: Fn() -> String, { - CustomTypeOp { closure } + CustomTypeOp { closure, description } } } -impl<'gcx, 'tcx, F, R> TypeOp<'gcx, 'tcx> for CustomTypeOp +impl<'gcx, 'tcx, F, R, G> TypeOp<'gcx, 'tcx> for CustomTypeOp where F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + G: Fn() -> String, { type Output = R; @@ -58,7 +61,16 @@ where } } -pub(super) trait InfcxTypeOp<'gcx, 'tcx>: Sized { +impl fmt::Debug for CustomTypeOp +where + G: Fn() -> String, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", (self.description)()) + } +} + +pub(super) trait InfcxTypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; /// Micro-optimization: returns `Ok(x)` if we can trivially @@ -87,6 +99,7 @@ where } } +#[derive(Debug)] pub(super) struct Subtype<'tcx> { param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, @@ -121,6 +134,7 @@ impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } +#[derive(Debug)] pub(super) struct Eq<'tcx> { param_env: ParamEnv<'tcx>, a: Ty<'tcx>, @@ -151,6 +165,7 @@ impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for Eq<'tcx> { } } +#[derive(Debug)] pub(super) struct ProvePredicates<'tcx> { obligations: Vec>, } @@ -188,6 +203,7 @@ impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { } } +#[derive(Debug)] pub(super) struct Normalize<'tcx, T> { param_env: ParamEnv<'tcx>, value: T, @@ -221,10 +237,7 @@ where .at(&ObligationCause::dummy(), self.param_env) .normalize(&self.value) .unwrap_or_else(|NoSolution| { - bug!( - "normalization of `{:?}` failed", - self.value, - ); + bug!("normalization of `{:?}` failed", self.value,); }); Ok(InferOk { value, obligations }) } From 252a6dfa354e15bd08e34618b811091ff57c14c9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 05:38:44 -0400 Subject: [PATCH 15/72] introduce `DropckOutlives` type-op --- .../borrow_check/nll/type_check/liveness.rs | 15 ++++------ .../borrow_check/nll/type_check/type_op.rs | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+), 10 deletions(-) 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 a032b2a9caabf..2640082c7cfed 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -9,7 +9,7 @@ // except according to those terms. use borrow_check::nll::region_infer::Cause; -use borrow_check::nll::type_check::type_op::CustomTypeOp; +use borrow_check::nll::type_check::type_op::DropckOutlives; use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; @@ -17,7 +17,6 @@ use dataflow::{FlowAtLocation, FlowsAtLocation}; use rustc::infer::region_constraints::RegionConstraintData; use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; -use rustc::traits::ObligationCause; use rustc::ty::subst::Kind; use rustc::ty::{Ty, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; @@ -217,15 +216,11 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo ) -> DropData<'tcx> { debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); + let param_env = cx.param_env; let (dropped_kinds, region_constraint_data) = - cx.fully_perform_op_and_get_region_constraint_data(CustomTypeOp::new( - |cx| { - Ok(cx - .infcx - .at(&ObligationCause::dummy(), cx.param_env) - .dropck_outlives(dropped_ty)) - }, - || format!("compute_drop_data(dropped_ty={:?})", dropped_ty), + cx.fully_perform_op_and_get_region_constraint_data(DropckOutlives::new( + param_env, + dropped_ty, )).unwrap(); DropData { diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs index ff008d39d49b2..cf4464905c077 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op.rs @@ -13,6 +13,7 @@ use rustc::infer::{InferCtxt, InferOk, InferResult}; use rustc::traits::query::NoSolution; use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation}; use rustc::ty::fold::TypeFoldable; +use rustc::ty::subst::Kind; use rustc::ty::{ParamEnv, Predicate, Ty}; use std::fmt; @@ -242,3 +243,32 @@ where Ok(InferOk { value, obligations }) } } + +#[derive(Debug)] +pub(super) struct DropckOutlives<'tcx> { + param_env: ParamEnv<'tcx>, + dropped_ty: Ty<'tcx>, +} + +impl<'tcx> DropckOutlives<'tcx> { + pub(super) fn new( + param_env: ParamEnv<'tcx>, + dropped_ty: Ty<'tcx>, + ) -> Self { + DropckOutlives { param_env, dropped_ty } + } +} + +impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { + type Output = Vec>; + + fn trivial_noop(self) -> Result { + Err(self) + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + Ok(infcx + .at(&ObligationCause::dummy(), self.param_env) + .dropck_outlives(self.dropped_ty)) + } +} From dee18864ad419036c8759cbdadb63cf132f139ef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 05:40:09 -0400 Subject: [PATCH 16/72] promote `type_op` into a `mod.rs` file --- .../borrow_check/nll/type_check/{type_op.rs => type_op/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/librustc_mir/borrow_check/nll/type_check/{type_op.rs => type_op/mod.rs} (100%) diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs similarity index 100% rename from src/librustc_mir/borrow_check/nll/type_check/type_op.rs rename to src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs From efc84c83f91e405830aea88ecab2a2495dd9e1db Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 05:52:07 -0400 Subject: [PATCH 17/72] remove the `TypeOp` vs `InfcxTypeOp` distinction --- .../nll/type_check/input_output.rs | 11 ++-- .../borrow_check/nll/type_check/mod.rs | 13 ++--- .../nll/type_check/type_op/mod.rs | 54 ++++--------------- 3 files changed, 23 insertions(+), 55 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index afc21948e4025..8144641a5efbf 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -76,12 +76,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { "equate_inputs_and_outputs: normalized output_ty={:?}", output_ty ); + let param_env = self.param_env; let mir_output_ty = mir.local_decls[RETURN_PLACE].ty; let anon_type_map = self.fully_perform_op( Locations::All, CustomTypeOp::new( - |cx| { + |infcx| { let mut obligations = ObligationAccumulator::default(); let dummy_body_id = ObligationCause::dummy().body_id; @@ -89,7 +90,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { obligations.add(infcx.instantiate_anon_types( mir_def_id, dummy_body_id, - cx.param_env, + param_env, &output_ty, )); debug!( @@ -107,7 +108,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); obligations.add( infcx - .at(&ObligationCause::dummy(), cx.param_env) + .at(&ObligationCause::dummy(), param_env) .eq(output_ty, mir_output_ty)?, ); @@ -115,7 +116,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let anon_defn_ty = tcx.type_of(anon_def_id); let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); let anon_defn_ty = renumber::renumber_regions( - cx.infcx, + infcx, TyContext::Location(Location::START), &anon_defn_ty, ); @@ -126,7 +127,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); obligations.add( infcx - .at(&ObligationCause::dummy(), cx.param_env) + .at(&ObligationCause::dummy(), param_env) .eq(anon_decl.concrete_ty, anon_defn_ty)?, ); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 959532fff8da5..f81b56de2483f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -783,23 +783,24 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { info!("fully_perform_op_and_get_region_constraint_data({:?})", op,); } - let mut fulfill_cx = TraitEngine::new(self.infcx.tcx); + let infcx = self.infcx; + let mut fulfill_cx = TraitEngine::new(infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; - let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op.perform(self))?; + let InferOk { value, obligations } = infcx.commit_if_ok(|_| op.perform(infcx))?; debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); - fulfill_cx.register_predicate_obligations(self.infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { + fulfill_cx.register_predicate_obligations(infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(infcx) { span_mirbug!(self, "", "errors selecting obligation: {:?}", e); } - self.infcx.process_registered_region_obligations( + infcx.process_registered_region_obligations( self.region_bound_pairs, self.implicit_region_bound, self.param_env, dummy_body_id, ); - let data = self.infcx.take_and_reset_region_constraints(); + let data = infcx.take_and_reset_region_constraints(); if data.is_empty() { Ok((value, None)) } else { diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index cf4464905c077..127841bec7c49 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::type_check::TypeChecker; use rustc::infer::{InferCtxt, InferOk, InferResult}; use rustc::traits::query::NoSolution; use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation}; @@ -24,11 +23,7 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { /// produce the output, else returns `Err(self)` back. fn trivial_noop(self) -> Result; - /// Produce a description of the operation for the debug logs. - fn perform( - self, - type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, - ) -> InferResult<'tcx, Self::Output>; + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; } pub(super) struct CustomTypeOp { @@ -39,7 +34,7 @@ pub(super) struct CustomTypeOp { impl CustomTypeOp { pub(super) fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self where - F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, G: Fn() -> String, { CustomTypeOp { closure, description } @@ -48,7 +43,7 @@ impl CustomTypeOp { impl<'gcx, 'tcx, F, R, G> TypeOp<'gcx, 'tcx> for CustomTypeOp where - F: FnOnce(&mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, G: Fn() -> String, { type Output = R; @@ -57,8 +52,8 @@ where Err(self) } - fn perform(self, type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { - (self.closure)(type_checker) + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { + (self.closure)(infcx) } } @@ -71,35 +66,6 @@ where } } -pub(super) trait InfcxTypeOp<'gcx, 'tcx>: Sized + fmt::Debug { - type Output; - - /// Micro-optimization: returns `Ok(x)` if we can trivially - /// produce the output, else returns `Err(self)` back. - fn trivial_noop(self) -> Result; - - /// Produce a description of the operation for the debug logs. - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; -} - -impl<'gcx, 'tcx, OP> TypeOp<'gcx, 'tcx> for OP -where - OP: InfcxTypeOp<'gcx, 'tcx>, -{ - type Output = OP::Output; - - fn trivial_noop(self) -> Result { - InfcxTypeOp::trivial_noop(self) - } - - fn perform( - self, - type_checker: &mut TypeChecker<'_, 'gcx, 'tcx>, - ) -> InferResult<'tcx, OP::Output> { - InfcxTypeOp::perform(self, type_checker.infcx) - } -} - #[derive(Debug)] pub(super) struct Subtype<'tcx> { param_env: ParamEnv<'tcx>, @@ -117,7 +83,7 @@ impl<'tcx> Subtype<'tcx> { } } -impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for Subtype<'tcx> { +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { type Output = (); fn trivial_noop(self) -> Result { @@ -148,7 +114,7 @@ impl<'tcx> Eq<'tcx> { } } -impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for Eq<'tcx> { +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { type Output = (); fn trivial_noop(self) -> Result { @@ -185,7 +151,7 @@ impl<'tcx> ProvePredicates<'tcx> { } } -impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { type Output = (); fn trivial_noop(self) -> Result { @@ -219,7 +185,7 @@ where } } -impl<'gcx, 'tcx, T> InfcxTypeOp<'gcx, 'tcx> for Normalize<'tcx, T> +impl<'gcx, 'tcx, T> TypeOp<'gcx, 'tcx> for Normalize<'tcx, T> where T: fmt::Debug + TypeFoldable<'tcx>, { @@ -259,7 +225,7 @@ impl<'tcx> DropckOutlives<'tcx> { } } -impl<'gcx, 'tcx> InfcxTypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { +impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { type Output = Vec>; fn trivial_noop(self) -> Result { From 6ac89174b8b1f75ff867588e82a51e7134aaba31 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 06:02:08 -0400 Subject: [PATCH 18/72] mk `fully_perform_op_and_get_region_constraint_data` a TypeOp method --- .../borrow_check/nll/type_check/liveness.rs | 11 +++- .../borrow_check/nll/type_check/mod.rs | 63 ++++-------------- .../nll/type_check/type_op/mod.rs | 64 ++++++++++++++++++- 3 files changed, 82 insertions(+), 56 deletions(-) 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 2640082c7cfed..9c854e38cc5b8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -9,7 +9,7 @@ // except according to those terms. use borrow_check::nll::region_infer::Cause; -use borrow_check::nll::type_check::type_op::DropckOutlives; +use borrow_check::nll::type_check::type_op::{DropckOutlives, TypeOp}; use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; @@ -218,10 +218,15 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo let param_env = cx.param_env; let (dropped_kinds, region_constraint_data) = - cx.fully_perform_op_and_get_region_constraint_data(DropckOutlives::new( + DropckOutlives::new( param_env, dropped_ty, - )).unwrap(); + ).fully_perform( + cx.infcx, + cx.region_bound_pairs, + cx.implicit_region_bound, + cx.param_env, + ).unwrap(); DropData { dropped_kinds, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index f81b56de2483f..e2f6ad0bde33f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -21,12 +21,12 @@ use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; use rustc::hir::def_id::DefId; use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; -use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::{InferCtxt, LateBoundRegionConversionTime, UnitResult}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; -use rustc::traits::{ObligationCause, TraitEngine}; +use rustc::traits::ObligationCause; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; @@ -733,18 +733,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { locations: Locations, op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { - match op.trivial_noop() { - Ok(r) => Ok(r), - Err(op) => { - let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data(op)?; - - if let Some(data) = opt_data { - self.push_region_constraints(locations, data); - } + let (r, opt_data) = op.fully_perform( + self.infcx, + self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + )?; - Ok(r) - } + if let Some(data) = opt_data { + self.push_region_constraints(locations, data); } + + Ok(r) } fn push_region_constraints( @@ -769,45 +769,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - /// Helper for `fully_perform_op`, but also used on its own - /// sometimes to enable better caching: executes `op` fully (along - /// with resulting obligations) and returns the full set of region - /// obligations. If the same `op` were to be performed at some - /// other location, then the same set of region obligations would - /// be generated there, so this can be useful for caching. - fn fully_perform_op_and_get_region_constraint_data( - &mut self, - op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, - ) -> Result<(R, Option>>), TypeError<'tcx>> { - if cfg!(debug_assertions) { - info!("fully_perform_op_and_get_region_constraint_data({:?})", op,); - } - - let infcx = self.infcx; - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - let dummy_body_id = ObligationCause::dummy().body_id; - let InferOk { value, obligations } = infcx.commit_if_ok(|_| op.perform(infcx))?; - debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); - fulfill_cx.register_predicate_obligations(infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(infcx) { - span_mirbug!(self, "", "errors selecting obligation: {:?}", e); - } - - infcx.process_registered_region_obligations( - self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - dummy_body_id, - ); - - let data = infcx.take_and_reset_region_constraints(); - if data.is_empty() { - Ok((value, None)) - } else { - Ok((value, Some(Rc::new(data)))) - } - } - fn sub_types( &mut self, sub: Ty<'tcx>, diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index 127841bec7c49..c3801b4fbac2f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -9,12 +9,16 @@ // except according to those terms. use rustc::infer::{InferCtxt, InferOk, InferResult}; +use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; use rustc::traits::query::NoSolution; -use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation}; +use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation, TraitEngine}; +use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Kind; -use rustc::ty::{ParamEnv, Predicate, Ty}; +use rustc::ty::{self, ParamEnv, Predicate, Ty}; +use std::rc::Rc; use std::fmt; +use syntax::codemap::DUMMY_SP; pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; @@ -23,9 +27,65 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { /// produce the output, else returns `Err(self)` back. fn trivial_noop(self) -> Result; + /// Given an infcx, performs **the kernel** of the operation: this does the + /// key action and then, optionally, returns a set of obligations which must be proven. + /// + /// This method is not meant to be invoked directly: instead, one + /// should use `fully_perform`, which will take those resulting + /// obligations and prove them, and then process the combined + /// results into region obligations which are returned. fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; + + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + #[inline(never)] + fn fully_perform( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ParamEnv<'tcx>, + ) -> Result<(Self::Output, Option>>), TypeError<'tcx>> { + let op = match self.trivial_noop() { + Ok(r) => return Ok((r, None)), + Err(op) => op, + }; + + if cfg!(debug_assertions) { + info!("fully_perform_op_and_get_region_constraint_data({:?})", op); + } + + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let dummy_body_id = ObligationCause::dummy().body_id; + let InferOk { value, obligations } = infcx.commit_if_ok(|_| op.perform(infcx))?; + debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); + fulfill_cx.register_predicate_obligations(infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(infcx) { + infcx.tcx.sess.diagnostic().delay_span_bug( + DUMMY_SP, + &format!("errors selecting obligation during MIR typeck: {:?}", e) + ); + } + + infcx.process_registered_region_obligations( + region_bound_pairs, + implicit_region_bound, + param_env, + dummy_body_id, + ); + + let data = infcx.take_and_reset_region_constraints(); + if data.is_empty() { + Ok((value, None)) + } else { + Ok((value, Some(Rc::new(data)))) + } + } } + + pub(super) struct CustomTypeOp { closure: F, description: G, From 71234278fadf24f89ae8a21d3a0418fccf7d3778 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Jun 2018 18:23:01 -0400 Subject: [PATCH 19/72] align the `type-op` outputs with what canonicalized queries give --- src/librustc/infer/outlives/mod.rs | 2 +- src/librustc/infer/outlives/obligations.rs | 4 +- .../nll/type_check/constraint_conversion.rs | 140 ++++++++++++------ .../borrow_check/nll/type_check/liveness.rs | 21 +-- .../borrow_check/nll/type_check/mod.rs | 47 ++++-- .../nll/type_check/type_op/mod.rs | 62 +++++--- 6 files changed, 175 insertions(+), 101 deletions(-) diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index 6aafebe79c671..93079b046690c 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -13,4 +13,4 @@ pub mod env; pub mod free_region_map; pub mod bounds; -mod obligations; +pub mod obligations; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index c0d530618eca1..07286f1250cd3 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -250,7 +250,7 @@ impl<'cx, 'gcx, 'tcx, D> TypeOutlives<'cx, 'gcx, 'tcx, D> where D: TypeOutlivesDelegate<'tcx>, { - fn new( + pub fn new( delegate: D, tcx: TyCtxt<'cx, 'gcx, 'tcx>, region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], @@ -274,7 +274,7 @@ where /// - `origin`, the reason we need this constraint /// - `ty`, the type `T` /// - `region`, the region `'a` - fn type_must_outlive( + pub fn type_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 06aaf6810faa3..44f8420ca7ed0 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -11,64 +11,66 @@ use borrow_check::location::LocationTable; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::{OutlivesConstraint, RegionTest, TypeTest}; -use borrow_check::nll::type_check::Locations; +use borrow_check::nll::type_check::{Locations, LexicalRegionConstraintData}; use borrow_check::nll::universal_regions::UniversalRegions; -use rustc::infer::region_constraints::Constraint; -use rustc::infer::region_constraints::RegionConstraintData; -use rustc::infer::region_constraints::{Verify, VerifyBound}; +use rustc::infer::{self, RegionObligation, SubregionOrigin}; +use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; +use rustc::infer::region_constraints::{Constraint, GenericKind, VerifyBound}; use rustc::mir::{Location, Mir}; -use rustc::ty; +use rustc::ty::{self, TyCtxt}; use syntax::codemap::Span; -crate struct ConstraintConversion<'a, 'tcx: 'a> { +crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a LocationTable, + region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + locations: Locations, outlives_constraints: &'a mut Vec, type_tests: &'a mut Vec>, all_facts: &'a mut Option, - } -impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { crate fn new( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a LocationTable, + region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + locations: Locations, outlives_constraints: &'a mut Vec, type_tests: &'a mut Vec>, all_facts: &'a mut Option, ) -> Self { Self { + tcx, mir, universal_regions, location_table, + region_bound_pairs, + implicit_region_bound, + param_env, + locations, outlives_constraints, type_tests, all_facts, } } - crate fn convert( - &mut self, - locations: Locations, - data: &RegionConstraintData<'tcx>, - ) { - debug!("generate: constraints at: {:#?}", locations); - let RegionConstraintData { + pub(super) fn convert(&mut self, data: &LexicalRegionConstraintData<'tcx>) { + debug!("generate: constraints at: {:#?}", self.locations); + let LexicalRegionConstraintData { constraints, - verifys, - givens, + region_obligations, } = data; - let span = self - .mir - .source_info(locations.from_location().unwrap_or(Location::START)) - .span; - - let at_location = locations.at_location().unwrap_or(Location::START); - - for constraint in constraints.keys() { + for constraint in constraints { debug!("generate: constraint: {:?}", constraint); let (a_vid, b_vid) = match constraint { Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid), @@ -84,13 +86,13 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // reverse direction, because `regioncx` talks about // "outlives" (`>=`) whereas the region constraints // talk about `<=`. - self.add_outlives(span, b_vid, a_vid, at_location); + self.add_outlives(b_vid, a_vid); // In the new analysis, all outlives relations etc // "take effect" at the mid point of the statement // that requires them, so ignore the `at_location`. if let Some(all_facts) = &mut self.all_facts { - if let Some(from_location) = locations.from_location() { + if let Some(from_location) = self.locations.from_location() { all_facts.outlives.push(( b_vid, a_vid, @@ -104,36 +106,50 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } - for verify in verifys { - let type_test = self.verify_to_type_test(verify, span, locations); - self.add_type_test(type_test); + let ConstraintConversion { + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + .. + } = *self; + for r_o in region_obligations { + let RegionObligation { + sup_type, + sub_region, + cause, + } = r_o; + + // we don't actually use this for anything. + let origin = infer::RelateParamBound(cause.span, sup_type); + + TypeOutlives::new( + &mut *self, + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ).type_must_outlive(origin, sup_type, sub_region); } - - assert!( - givens.is_empty(), - "MIR type-checker does not use givens (thank goodness)" - ); } fn verify_to_type_test( &self, - verify: &Verify<'tcx>, - span: Span, - locations: Locations, + generic_kind: GenericKind<'tcx>, + region: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, ) -> TypeTest<'tcx> { - let generic_kind = verify.kind; + let lower_bound = self.to_region_vid(region); - let lower_bound = self.to_region_vid(verify.region); + let point = self.locations.at_location().unwrap_or(Location::START); - let point = locations.at_location().unwrap_or(Location::START); - - let test = self.verify_bound_to_region_test(&verify.bound); + let test = self.verify_bound_to_region_test(&bound); TypeTest { generic_kind, lower_bound, point, - span, + span: self.span(), test, } } @@ -168,13 +184,21 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.universal_regions.to_region_vid(r) } + fn span(&self) -> Span { + self + .mir + .source_info(self.locations.from_location().unwrap_or(Location::START)) + .span + } + fn add_outlives( &mut self, - span: Span, sup: ty::RegionVid, sub: ty::RegionVid, - point: Location, ) { + let span = self.span(); + let point = self.locations.at_location().unwrap_or(Location::START); + self.outlives_constraints.push(OutlivesConstraint { span, sub, @@ -188,3 +212,27 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.type_tests.push(type_test); } } + +impl<'a, 'b, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'gcx, 'tcx> { + fn push_sub_region_constraint( + &mut self, + _origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) { + let b = self.universal_regions.to_region_vid(b); + let a = self.universal_regions.to_region_vid(a); + self.add_outlives(b, a); + } + + fn push_verify( + &mut self, + _origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + let type_test = self.verify_to_type_test(kind, a, bound); + self.add_type_test(type_test); + } +} 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 9c854e38cc5b8..92a60602b796a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -10,11 +10,10 @@ use borrow_check::nll::region_infer::Cause; use borrow_check::nll::type_check::type_op::{DropckOutlives, TypeOp}; -use borrow_check::nll::type_check::AtLocation; +use borrow_check::nll::type_check::{AtLocation, LexicalRegionConstraintData}; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; -use rustc::infer::region_constraints::RegionConstraintData; use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::ty::subst::Kind; @@ -71,7 +70,7 @@ where struct DropData<'tcx> { dropped_kinds: Vec>, - region_constraint_data: Option>>, + region_constraint_data: Option>>, } impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { @@ -198,8 +197,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo }); if let Some(data) = &drop_data.region_constraint_data { - self.cx - .push_region_constraints(location.at_self(), data.clone()); + self.cx.push_region_constraints(location.at_self(), data); } // All things in the `outlives` array may be touched by @@ -217,16 +215,9 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); let param_env = cx.param_env; - let (dropped_kinds, region_constraint_data) = - DropckOutlives::new( - param_env, - dropped_ty, - ).fully_perform( - cx.infcx, - cx.region_bound_pairs, - cx.implicit_region_bound, - cx.param_env, - ).unwrap(); + let (dropped_kinds, region_constraint_data) = DropckOutlives::new(param_env, dropped_ty) + .fully_perform(cx.infcx) + .unwrap(); DropData { dropped_kinds, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index e2f6ad0bde33f..cb16461de4914 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -20,8 +20,8 @@ use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; use rustc::hir::def_id::DefId; -use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; -use rustc::infer::{InferCtxt, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::region_constraints::{Constraint, GenericKind}; +use rustc::infer::{InferCtxt, LateBoundRegionConversionTime, RegionObligation, UnitResult}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; @@ -31,7 +31,6 @@ use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use std::fmt; -use std::rc::Rc; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; use util::liveness::LivenessResults; @@ -626,6 +625,21 @@ crate struct MirTypeckRegionConstraints<'tcx> { crate type_tests: Vec>, } +/// The type checker layers on top of the "old" inference engine. The +/// idea is that we run some operations, like trait selection, and +/// then we "scrape out" the region constraints that have accumulated +/// from the old lexical solver. This struct just collects the bits of +/// that data that we care about into one place. +#[derive(Debug)] +struct LexicalRegionConstraintData<'tcx> { + /// The `'a <= 'b` constraints extracted from `RegionConstraintData`. + constraints: Vec>, + + /// The `T: 'a` (and `'a: 'b`, in some cases) constraints + /// extracted from the pending "region obligations". + region_obligations: Vec>, +} + /// The `Locations` type summarizes *where* region constraints are /// required to hold. Normally, this is at a particular point which /// created the obligation, but for constraints that the user gave, we @@ -733,14 +747,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { locations: Locations, op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, ) -> Result> { - let (r, opt_data) = op.fully_perform( - self.infcx, - self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - )?; - - if let Some(data) = opt_data { + let (r, opt_data) = op.fully_perform(self.infcx)?; + + if let Some(data) = &opt_data { self.push_region_constraints(locations, data); } @@ -750,7 +759,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn push_region_constraints( &mut self, locations: Locations, - data: Rc>, + data: &LexicalRegionConstraintData<'tcx>, ) { debug!( "push_region_constraints: constraints generated at {:?} are {:#?}", @@ -759,13 +768,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(borrowck_context) = &mut self.borrowck_context { constraint_conversion::ConstraintConversion::new( + self.infcx.tcx, self.mir, borrowck_context.universal_regions, borrowck_context.location_table, + self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + locations, &mut self.constraints.outlives_constraints, &mut self.constraints.type_tests, &mut borrowck_context.all_facts, - ).convert(locations, &data); + ).convert(&data); } } @@ -1689,3 +1703,10 @@ impl ToLocations for Location { self.at_self() } } + +impl<'tcx> LexicalRegionConstraintData<'tcx> { + fn is_empty(&self) -> bool { + let LexicalRegionConstraintData { constraints, region_obligations } = self; + constraints.is_empty() && region_obligations.is_empty() + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index c3801b4fbac2f..263bce8067f2d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -8,16 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use borrow_check::nll::type_check::LexicalRegionConstraintData; +use rustc::infer::region_constraints::RegionConstraintData; use rustc::infer::{InferCtxt, InferOk, InferResult}; -use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; use rustc::traits::query::NoSolution; use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation, TraitEngine}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Kind; -use rustc::ty::{self, ParamEnv, Predicate, Ty}; -use std::rc::Rc; +use rustc::ty::{ParamEnv, Predicate, Ty}; use std::fmt; +use std::rc::Rc; use syntax::codemap::DUMMY_SP; pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { @@ -43,10 +44,7 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], - implicit_region_bound: Option>, - param_env: ParamEnv<'tcx>, - ) -> Result<(Self::Output, Option>>), TypeError<'tcx>> { + ) -> Result<(Self::Output, Option>>), TypeError<'tcx>> { let op = match self.trivial_noop() { Ok(r) => return Ok((r, None)), Err(op) => op, @@ -68,14 +66,29 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { ); } - infcx.process_registered_region_obligations( - region_bound_pairs, - implicit_region_bound, - param_env, - dummy_body_id, - ); + let region_obligations: Vec<_> = infcx + .take_registered_region_obligations() + .into_iter() + .map(|(_node_id, region_obligation)| region_obligation) + .collect(); + + let RegionConstraintData { + constraints, + verifys, + givens, + } = infcx.take_and_reset_region_constraints(); - let data = infcx.take_and_reset_region_constraints(); + // These are created when we "process" the registered region + // obliations, and that hasn't happened yet. + assert!(verifys.is_empty()); + + // NLL doesn't use givens (and thank goodness!). + assert!(givens.is_empty()); + + let data = LexicalRegionConstraintData { + constraints: constraints.keys().cloned().collect(), + region_obligations, + }; if data.is_empty() { Ok((value, None)) } else { @@ -84,8 +97,6 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { } } - - pub(super) struct CustomTypeOp { closure: F, description: G, @@ -97,7 +108,10 @@ impl CustomTypeOp { F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, G: Fn() -> String, { - CustomTypeOp { closure, description } + CustomTypeOp { + closure, + description, + } } } @@ -277,11 +291,11 @@ pub(super) struct DropckOutlives<'tcx> { } impl<'tcx> DropckOutlives<'tcx> { - pub(super) fn new( - param_env: ParamEnv<'tcx>, - dropped_ty: Ty<'tcx>, - ) -> Self { - DropckOutlives { param_env, dropped_ty } + pub(super) fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { + DropckOutlives { + param_env, + dropped_ty, + } } } @@ -294,7 +308,7 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { Ok(infcx - .at(&ObligationCause::dummy(), self.param_env) - .dropck_outlives(self.dropped_ty)) + .at(&ObligationCause::dummy(), self.param_env) + .dropck_outlives(self.dropped_ty)) } } From 5e9f8d5c69b3d90f84867001769a5feef538dbdb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 08:51:21 -0400 Subject: [PATCH 20/72] promote canonical into a module --- src/librustc/infer/{canonical.rs => canonical/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/librustc/infer/{canonical.rs => canonical/mod.rs} (100%) diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical/mod.rs similarity index 100% rename from src/librustc/infer/canonical.rs rename to src/librustc/infer/canonical/mod.rs From a1811cef764ebae9715947066c19ea171b7927f1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 08:59:26 -0400 Subject: [PATCH 21/72] break canonicalizer into submodules to make it easier to comprehend --- src/librustc/infer/canonical/canonicalizer.rs | 347 ++++++++++ src/librustc/infer/canonical/mod.rs | 608 +----------------- src/librustc/infer/canonical/query_result.rs | 215 +++++++ src/librustc/infer/canonical/substitute.rs | 113 ++++ 4 files changed, 689 insertions(+), 594 deletions(-) create mode 100644 src/librustc/infer/canonical/canonicalizer.rs create mode 100644 src/librustc/infer/canonical/query_result.rs create mode 100644 src/librustc/infer/canonical/substitute.rs diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs new file mode 100644 index 0000000000000..677063fcf881e --- /dev/null +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -0,0 +1,347 @@ +// 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. + +//! This module contains the "canonicalizer" itself. +//! +//! For an overview of what canonicaliation is and how it fits into +//! rustc, check out the [chapter in the rustc guide][c]. +//! +//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html + +use infer::canonical::{ + Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, CanonicalVarValues, + Canonicalize, +}; +use infer::InferCtxt; +use std::sync::atomic::Ordering; +use ty::fold::{TypeFoldable, TypeFolder}; +use ty::subst::Kind; +use ty::{self, CanonicalVar, Slice, Ty, TyCtxt, TypeFlags}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexed_vec::IndexVec; + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Canonicalizes a query value `V`. When we canonicalize a query, + /// we not only canonicalize unbound inference variables, but we + /// *also* replace all free regions whatsoever. So for example a + /// query like `T: Trait<'static>` would be canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc guide][c]. + /// + /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query + pub fn canonicalize_query(&self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + where + V: Canonicalize<'gcx, 'tcx>, + { + self.tcx + .sess + .perf_stats + .queries_canonicalized + .fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + CanonicalizeAllFreeRegions(true), + ) + } + + /// Canonicalizes a query *response* `V`. When we canonicalize a + /// query response, we only canonicalize unbound inference + /// variables, and we leave other free regions alone. So, + /// continuing with the example from `canonicalize_query`, if + /// there was an input query `T: Trait<'static>`, it would have + /// been canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. But if we found that there + /// exists only one possible impl of `Trait`, and it looks like + /// + /// impl Trait<'static> for T { .. } + /// + /// then we would prepare a query result R that (among other + /// things) includes a mapping to `'?0 := 'static`. When + /// canonicalizing this query result R, we would leave this + /// reference to `'static` alone. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc guide][c]. + /// + /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result + pub fn canonicalize_response( + &self, + value: &V, + ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + where + V: Canonicalize<'gcx, 'tcx>, + { + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + CanonicalizeAllFreeRegions(false), + ) + } +} + +/// If this flag is true, then all free regions will be replaced with +/// a canonical var. This is used to make queries as generic as +/// possible. For example, the query `F: Foo<'static>` would be +/// canonicalized to `F: Foo<'0>`. +struct CanonicalizeAllFreeRegions(pub bool); + +struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + variables: IndexVec, + indices: FxHashMap, CanonicalVar>, + var_values: IndexVec>, + canonicalize_all_free_regions: CanonicalizeAllFreeRegions, + needs_canonical_flags: TypeFlags, +} + +impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReLateBound(..) => { + // leave bound regions alone + r + } + + ty::ReVar(vid) => { + let r = self + .infcx + .unwrap() + .borrow_region_constraints() + .opportunistic_resolve_var(self.tcx, vid); + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Region, + }; + debug!( + "canonical: region var found with vid {:?}, \ + opportunistically resolved to {:?}", + vid, r + ); + let cvar = self.canonical_var(info, r.into()); + self.tcx().mk_region(ty::ReCanonical(cvar)) + } + + ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(_) + | ty::ReScope(_) + | ty::ReSkolemized(..) + | ty::ReEmpty + | ty::ReErased => { + if self.canonicalize_all_free_regions.0 { + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Region, + }; + let cvar = self.canonical_var(info, r.into()); + self.tcx().mk_region(ty::ReCanonical(cvar)) + } else { + r + } + } + + ty::ReClosureBound(..) | ty::ReCanonical(_) => { + bug!("canonical region encountered during canonicalization") + } + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.sty { + ty::TyInfer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t), + + ty::TyInfer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t), + + ty::TyInfer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t), + + ty::TyInfer(ty::FreshTy(_)) + | ty::TyInfer(ty::FreshIntTy(_)) + | ty::TyInfer(ty::FreshFloatTy(_)) => { + bug!("encountered a fresh type during canonicalization") + } + + ty::TyInfer(ty::CanonicalTy(_)) => { + bug!("encountered a canonical type during canonicalization") + } + + ty::TyClosure(..) + | ty::TyGenerator(..) + | ty::TyGeneratorWitness(..) + | ty::TyBool + | ty::TyChar + | ty::TyInt(..) + | ty::TyUint(..) + | ty::TyFloat(..) + | ty::TyAdt(..) + | ty::TyStr + | ty::TyError + | ty::TyArray(..) + | ty::TySlice(..) + | ty::TyRawPtr(..) + | ty::TyRef(..) + | ty::TyFnDef(..) + | ty::TyFnPtr(_) + | ty::TyDynamic(..) + | ty::TyNever + | ty::TyTuple(..) + | ty::TyProjection(..) + | ty::TyForeign(..) + | ty::TyParam(..) + | ty::TyAnon(..) => { + if t.flags.intersects(self.needs_canonical_flags) { + t.super_fold_with(self) + } else { + t + } + } + } + } +} + +impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { + /// The main `canonicalize` method, shared impl of + /// `canonicalize_query` and `canonicalize_response`. + fn canonicalize( + value: &V, + infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + canonicalize_all_free_regions: CanonicalizeAllFreeRegions, + ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + where + V: Canonicalize<'gcx, 'tcx>, + { + debug_assert!( + !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS), + "canonicalizing a canonical value: {:?}", + value, + ); + + let needs_canonical_flags = if canonicalize_all_free_regions.0 { + TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX + } else { + TypeFlags::KEEP_IN_LOCAL_TCX + }; + + let gcx = tcx.global_tcx(); + + // Fast path: nothing that needs to be canonicalized. + if !value.has_type_flags(needs_canonical_flags) { + let out_value = gcx.lift(value).unwrap(); + let canon_value = V::intern( + gcx, + Canonical { + variables: Slice::empty(), + value: out_value, + }, + ); + let values = CanonicalVarValues { + var_values: IndexVec::default(), + }; + return (canon_value, values); + } + + let mut canonicalizer = Canonicalizer { + infcx, + tcx, + canonicalize_all_free_regions, + needs_canonical_flags, + variables: IndexVec::default(), + indices: FxHashMap::default(), + var_values: IndexVec::default(), + }; + let out_value = value.fold_with(&mut canonicalizer); + + // Once we have canonicalized `out_value`, it should not + // contain anything that ties it to this inference context + // anymore, so it should live in the global arena. + let out_value = gcx.lift(&out_value).unwrap_or_else(|| { + bug!( + "failed to lift `{:?}`, canonicalized from `{:?}`", + out_value, + value + ) + }); + + let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables.raw); + + let canonical_value = V::intern( + gcx, + Canonical { + variables: canonical_variables, + value: out_value, + }, + ); + let canonical_var_values = CanonicalVarValues { + var_values: canonicalizer.var_values, + }; + (canonical_value, canonical_var_values) + } + + /// Creates a canonical variable replacing `kind` from the input, + /// or returns an existing variable if `kind` has already been + /// seen. `kind` is expected to be an unbound variable (or + /// potentially a free region). + fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar { + let Canonicalizer { + indices, + variables, + var_values, + .. + } = self; + + indices + .entry(kind) + .or_insert_with(|| { + let cvar1 = variables.push(info); + let cvar2 = var_values.push(kind); + assert_eq!(cvar1, cvar2); + cvar1 + }) + .clone() + } + + /// Given a type variable `ty_var` of the given kind, first check + /// if `ty_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `ty_var`. + fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> { + let infcx = self.infcx.expect("encountered ty-var without infcx"); + let bound_to = infcx.shallow_resolve(ty_var); + if bound_to != ty_var { + self.fold_ty(bound_to) + } else { + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Ty(ty_kind), + }; + let cvar = self.canonical_var(info, ty_var.into()); + self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar)) + } + } +} diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index ef11cc0f4932e..037fc637ec91e 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -31,20 +31,22 @@ //! //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html -use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin}; -use rustc_data_structures::indexed_vec::Idx; +use infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; use serialize::UseSpecializedDecodable; use std::fmt::Debug; use std::ops::Index; -use std::sync::atomic::Ordering; use syntax::codemap::Span; -use traits::{Obligation, ObligationCause, PredicateObligation}; -use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags}; -use ty::subst::{Kind, UnpackedKind}; -use ty::fold::{TypeFoldable, TypeFolder}; +use ty::{self, CanonicalVar, Lift, Region, Slice, TyCtxt}; +use ty::subst::Kind; +use ty::fold::TypeFoldable; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::fx::FxHashMap; + +mod canonicalizer; + +mod query_result; + +mod substitute; /// A "canonicalized" type `V` is one where all free inference /// variables have been rewriten to "canonical vars". These are @@ -66,11 +68,8 @@ impl<'gcx> UseSpecializedDecodable for CanonicalVarInfos<'gcx> { } /// /// When you canonicalize a value `V`, you get back one of these /// vectors with the original values that were replaced by canonical -/// variables. -/// -/// You can also use `infcx.fresh_inference_vars_for_canonical_vars` -/// to get back a `CanonicalVarValues` containing fresh inference -/// variables. +/// variables. You will need to supply it later to instantiate the +/// canonicalized query response. #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct CanonicalVarValues<'tcx> { pub var_values: IndexVec>, @@ -223,7 +222,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// canonical, creates fresh inference variables with the same /// characteristics. You can then use `substitute` to instantiate /// the canonical variable with these inference variables. - pub fn fresh_inference_vars_for_canonical_vars( + fn fresh_inference_vars_for_canonical_vars( &self, span: Span, variables: &Slice, @@ -238,7 +237,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Given the "info" about a canonical variable, creates a fresh /// inference variable with the same characteristics. - pub fn fresh_inference_var_for_canonical_var( + fn fresh_inference_var_for_canonical_var( &self, span: Span, cv_info: CanonicalVarInfo, @@ -264,585 +263,6 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } } } - - /// Given the (canonicalized) result to a canonical query, - /// instantiates the result so it can be used, plugging in the - /// values from the canonical query. (Note that the result may - /// have been ambiguous; you should check the certainty level of - /// the query before applying this function.) - /// - /// To get a good understanding of what is happening here, check - /// out the [chapter in the rustc guide][c]. - /// - /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#processing-the-canonicalized-query-result - pub fn instantiate_query_result( - &self, - cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - original_values: &CanonicalVarValues<'tcx>, - query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, - ) -> InferResult<'tcx, R> - where - R: Debug + TypeFoldable<'tcx>, - { - debug!( - "instantiate_query_result(original_values={:#?}, query_result={:#?})", - original_values, query_result, - ); - - // Every canonical query result includes values for each of - // the inputs to the query. Therefore, we begin by unifying - // these values with the original inputs that were - // canonicalized. - let result_values = &query_result.value.var_values; - assert_eq!(original_values.len(), result_values.len()); - - // Quickly try to find initial values for the canonical - // variables in the result in terms of the query. We do this - // by iterating down the values that the query gave to each of - // the canonical inputs. If we find that one of those values - // is directly equal to one of the canonical variables in the - // result, then we can type the corresponding value from the - // input. See the example above. - let mut opt_values: IndexVec>> = - IndexVec::from_elem_n(None, query_result.variables.len()); - - // In terms of our example above, we are iterating over pairs like: - // [(?A, Vec), ('static, '?1), (?B, ?0)] - for (original_value, result_value) in original_values.iter().zip(result_values) { - match result_value.unpack() { - UnpackedKind::Type(result_value) => { - // e.g., here `result_value` might be `?0` in the example above... - if let ty::TyInfer(ty::InferTy::CanonicalTy(index)) = result_value.sty { - // in which case we would set `canonical_vars[0]` to `Some(?U)`. - opt_values[index] = Some(original_value); - } - } - UnpackedKind::Lifetime(result_value) => { - // e.g., here `result_value` might be `'?1` in the example above... - if let &ty::RegionKind::ReCanonical(index) = result_value { - // in which case we would set `canonical_vars[0]` to `Some('static)`. - opt_values[index] = Some(original_value); - } - } - } - } - - // Create a result substitution: if we found a value for a - // given variable in the loop above, use that. Otherwise, use - // a fresh inference variable. - let result_subst = &CanonicalVarValues { - var_values: query_result - .variables - .iter() - .enumerate() - .map(|(index, info)| match opt_values[CanonicalVar::new(index)] { - Some(k) => k, - None => self.fresh_inference_var_for_canonical_var(cause.span, *info), - }) - .collect(), - }; - - // Unify the original values for the canonical variables in - // the input with the value found in the query - // post-substitution. Often, but not always, this is a no-op, - // because we already found the mapping in the first step. - let substituted_values = |index: CanonicalVar| -> Kind<'tcx> { - query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index]) - }; - let mut obligations = - self.unify_canonical_vars(cause, param_env, original_values, substituted_values)? - .into_obligations(); - - obligations.extend(self.query_region_constraints_into_obligations( - cause, - param_env, - &query_result.value.region_constraints, - result_subst, - )); - - let user_result: R = - query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value); - - Ok(InferOk { - value: user_result, - obligations, - }) - } - - /// Converts the region constraints resulting from a query into an - /// iterator of obligations. - fn query_region_constraints_into_obligations<'a>( - &'a self, - cause: &'a ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>], - result_subst: &'a CanonicalVarValues<'tcx>, - ) -> impl Iterator> + 'a { - Box::new(unsubstituted_region_constraints.iter().map(move |constraint| { - let ty::OutlivesPredicate(k1, r2) = constraint.skip_binder(); // restored below - let k1 = substitute_value(self.tcx, result_subst, k1); - let r2 = substitute_value(self.tcx, result_subst, r2); - match k1.unpack() { - UnpackedKind::Lifetime(r1) => - Obligation::new( - cause.clone(), - param_env, - ty::Predicate::RegionOutlives( - ty::Binder::dummy(ty::OutlivesPredicate(r1, r2))), - ), - - UnpackedKind::Type(t1) => - Obligation::new( - cause.clone(), - param_env, - ty::Predicate::TypeOutlives( - ty::Binder::dummy(ty::OutlivesPredicate(t1, r2))), - ), - } - })) as Box> - } - - /// Given two sets of values for the same set of canonical variables, unify them. - /// The second set is produced lazilly by supplying indices from the first set. - fn unify_canonical_vars( - &self, - cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - variables1: &CanonicalVarValues<'tcx>, - variables2: impl Fn(CanonicalVar) -> Kind<'tcx>, - ) -> InferResult<'tcx, ()> { - self.commit_if_ok(|_| { - let mut obligations = vec![]; - for (index, value1) in variables1.var_values.iter_enumerated() { - let value2 = variables2(index); - - match (value1.unpack(), value2.unpack()) { - (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { - obligations - .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); - } - ( - UnpackedKind::Lifetime(ty::ReErased), - UnpackedKind::Lifetime(ty::ReErased), - ) => { - // no action needed - } - (UnpackedKind::Lifetime(v1), UnpackedKind::Lifetime(v2)) => { - obligations - .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); - } - _ => { - bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,); - } - } - } - Ok(InferOk { - value: (), - obligations, - }) - }) - } - - /// Canonicalizes a query value `V`. When we canonicalize a query, - /// we not only canonicalize unbound inference variables, but we - /// *also* replace all free regions whatsoever. So for example a - /// query like `T: Trait<'static>` would be canonicalized to - /// - /// ```text - /// T: Trait<'?0> - /// ``` - /// - /// with a mapping M that maps `'?0` to `'static`. - /// - /// To get a good understanding of what is happening here, check - /// out the [chapter in the rustc guide][c]. - /// - /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query - pub fn canonicalize_query(&self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'tcx>) - where - V: Canonicalize<'gcx, 'tcx>, - { - self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); - - Canonicalizer::canonicalize( - value, - Some(self), - self.tcx, - CanonicalizeAllFreeRegions(true), - ) - } - - /// Canonicalizes a query *response* `V`. When we canonicalize a - /// query response, we only canonicalize unbound inference - /// variables, and we leave other free regions alone. So, - /// continuing with the example from `canonicalize_query`, if - /// there was an input query `T: Trait<'static>`, it would have - /// been canonicalized to - /// - /// ```text - /// T: Trait<'?0> - /// ``` - /// - /// with a mapping M that maps `'?0` to `'static`. But if we found that there - /// exists only one possible impl of `Trait`, and it looks like - /// - /// impl Trait<'static> for T { .. } - /// - /// then we would prepare a query result R that (among other - /// things) includes a mapping to `'?0 := 'static`. When - /// canonicalizing this query result R, we would leave this - /// reference to `'static` alone. - /// - /// To get a good understanding of what is happening here, check - /// out the [chapter in the rustc guide][c]. - /// - /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result - pub fn canonicalize_response( - &self, - value: &V, - ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) - where - V: Canonicalize<'gcx, 'tcx>, - { - Canonicalizer::canonicalize( - value, - Some(self), - self.tcx, - CanonicalizeAllFreeRegions(false), - ) - } -} - -/// If this flag is true, then all free regions will be replaced with -/// a canonical var. This is used to make queries as generic as -/// possible. For example, the query `F: Foo<'static>` would be -/// canonicalized to `F: Foo<'0>`. -struct CanonicalizeAllFreeRegions(bool); - -struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, - tcx: TyCtxt<'cx, 'gcx, 'tcx>, - variables: IndexVec, - indices: FxHashMap, CanonicalVar>, - var_values: IndexVec>, - canonicalize_all_free_regions: CanonicalizeAllFreeRegions, - needs_canonical_flags: TypeFlags, -} - -impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReLateBound(..) => { - // leave bound regions alone - r - } - - ty::ReVar(vid) => { - let r = self.infcx - .unwrap() - .borrow_region_constraints() - .opportunistic_resolve_var(self.tcx, vid); - let info = CanonicalVarInfo { - kind: CanonicalVarKind::Region, - }; - debug!( - "canonical: region var found with vid {:?}, \ - opportunistically resolved to {:?}", - vid, r - ); - let cvar = self.canonical_var(info, r.into()); - self.tcx().mk_region(ty::ReCanonical(cvar)) - } - - ty::ReStatic - | ty::ReEarlyBound(..) - | ty::ReFree(_) - | ty::ReScope(_) - | ty::ReSkolemized(..) - | ty::ReEmpty - | ty::ReErased => { - if self.canonicalize_all_free_regions.0 { - let info = CanonicalVarInfo { - kind: CanonicalVarKind::Region, - }; - let cvar = self.canonical_var(info, r.into()); - self.tcx().mk_region(ty::ReCanonical(cvar)) - } else { - r - } - } - - ty::ReClosureBound(..) | ty::ReCanonical(_) => { - bug!("canonical region encountered during canonicalization") - } - } - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.sty { - ty::TyInfer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t), - - ty::TyInfer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t), - - ty::TyInfer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t), - - ty::TyInfer(ty::FreshTy(_)) - | ty::TyInfer(ty::FreshIntTy(_)) - | ty::TyInfer(ty::FreshFloatTy(_)) => { - bug!("encountered a fresh type during canonicalization") - } - - ty::TyInfer(ty::CanonicalTy(_)) => { - bug!("encountered a canonical type during canonicalization") - } - - ty::TyClosure(..) - | ty::TyGenerator(..) - | ty::TyGeneratorWitness(..) - | ty::TyBool - | ty::TyChar - | ty::TyInt(..) - | ty::TyUint(..) - | ty::TyFloat(..) - | ty::TyAdt(..) - | ty::TyStr - | ty::TyError - | ty::TyArray(..) - | ty::TySlice(..) - | ty::TyRawPtr(..) - | ty::TyRef(..) - | ty::TyFnDef(..) - | ty::TyFnPtr(_) - | ty::TyDynamic(..) - | ty::TyNever - | ty::TyTuple(..) - | ty::TyProjection(..) - | ty::TyForeign(..) - | ty::TyParam(..) - | ty::TyAnon(..) => { - if t.flags.intersects(self.needs_canonical_flags) { - t.super_fold_with(self) - } else { - t - } - } - } - } -} - -impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { - /// The main `canonicalize` method, shared impl of - /// `canonicalize_query` and `canonicalize_response`. - fn canonicalize( - value: &V, - infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, - tcx: TyCtxt<'cx, 'gcx, 'tcx>, - canonicalize_all_free_regions: CanonicalizeAllFreeRegions, - ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) - where - V: Canonicalize<'gcx, 'tcx>, - { - debug_assert!( - !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS), - "canonicalizing a canonical value: {:?}", - value, - ); - - let needs_canonical_flags = if canonicalize_all_free_regions.0 { - TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX - } else { - TypeFlags::KEEP_IN_LOCAL_TCX - }; - - let gcx = tcx.global_tcx(); - - // Fast path: nothing that needs to be canonicalized. - if !value.has_type_flags(needs_canonical_flags) { - let out_value = gcx.lift(value).unwrap(); - let canon_value = V::intern( - gcx, - Canonical { - variables: Slice::empty(), - value: out_value, - }, - ); - let values = CanonicalVarValues { - var_values: IndexVec::default(), - }; - return (canon_value, values); - } - - let mut canonicalizer = Canonicalizer { - infcx, - tcx, - canonicalize_all_free_regions, - needs_canonical_flags, - variables: IndexVec::default(), - indices: FxHashMap::default(), - var_values: IndexVec::default(), - }; - let out_value = value.fold_with(&mut canonicalizer); - - // Once we have canonicalized `out_value`, it should not - // contain anything that ties it to this inference context - // anymore, so it should live in the global arena. - let out_value = gcx.lift(&out_value).unwrap_or_else(|| { - bug!( - "failed to lift `{:?}`, canonicalized from `{:?}`", - out_value, - value - ) - }); - - let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables.raw); - - let canonical_value = V::intern( - gcx, - Canonical { - variables: canonical_variables, - value: out_value, - }, - ); - let canonical_var_values = CanonicalVarValues { - var_values: canonicalizer.var_values, - }; - (canonical_value, canonical_var_values) - } - - /// Creates a canonical variable replacing `kind` from the input, - /// or returns an existing variable if `kind` has already been - /// seen. `kind` is expected to be an unbound variable (or - /// potentially a free region). - fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar { - let Canonicalizer { - indices, - variables, - var_values, - .. - } = self; - - indices - .entry(kind) - .or_insert_with(|| { - let cvar1 = variables.push(info); - let cvar2 = var_values.push(kind); - assert_eq!(cvar1, cvar2); - cvar1 - }) - .clone() - } - - /// Given a type variable `ty_var` of the given kind, first check - /// if `ty_var` is bound to anything; if so, canonicalize - /// *that*. Otherwise, create a new canonical variable for - /// `ty_var`. - fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> { - let infcx = self.infcx.expect("encountered ty-var without infcx"); - let bound_to = infcx.shallow_resolve(ty_var); - if bound_to != ty_var { - self.fold_ty(bound_to) - } else { - let info = CanonicalVarInfo { - kind: CanonicalVarKind::Ty(ty_kind), - }; - let cvar = self.canonical_var(info, ty_var.into()); - self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar)) - } - } -} - -impl<'tcx, V> Canonical<'tcx, V> { - /// Instantiate the wrapped value, replacing each canonical value - /// with the value given in `var_values`. - fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V - where - V: TypeFoldable<'tcx>, - { - self.substitute_projected(tcx, var_values, |value| value) - } - - /// Invoke `projection_fn` with `self.value` to get a value V that - /// is expressed in terms of the same canonical variables bound in - /// `self`. Apply the substitution `var_values` to this value V, - /// replacing each of the canonical variables. - fn substitute_projected( - &self, - tcx: TyCtxt<'_, '_, 'tcx>, - var_values: &CanonicalVarValues<'tcx>, - projection_fn: impl FnOnce(&V) -> &T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - assert_eq!(self.variables.len(), var_values.var_values.len()); - let value = projection_fn(&self.value); - substitute_value(tcx, var_values, value) - } -} - -/// Substitute the values from `var_values` into `value`. `var_values` -/// must be values for the set of cnaonical variables that appear in -/// `value`. -fn substitute_value<'a, 'tcx, T>( - tcx: TyCtxt<'_, '_, 'tcx>, - var_values: &CanonicalVarValues<'tcx>, - value: &'a T, -) -> T -where - T: TypeFoldable<'tcx>, -{ - if var_values.var_values.is_empty() { - debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS)); - value.clone() - } else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) { - value.clone() - } else { - value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values }) - } -} - -struct CanonicalVarValuesSubst<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - tcx: TyCtxt<'cx, 'gcx, 'tcx>, - var_values: &'cx CanonicalVarValues<'tcx>, -} - -impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'gcx, 'tcx> { - fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.sty { - ty::TyInfer(ty::InferTy::CanonicalTy(c)) => { - match self.var_values.var_values[c].unpack() { - UnpackedKind::Type(ty) => ty, - r => bug!("{:?} is a type but value is {:?}", c, r), - } - } - _ => { - if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) { - t - } else { - t.super_fold_with(self) - } - } - } - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match r { - ty::RegionKind::ReCanonical(c) => match self.var_values.var_values[*c].unpack() { - UnpackedKind::Lifetime(l) => l, - r => bug!("{:?} is a region but value is {:?}", c, r), - }, - _ => r.super_fold_with(self), - } - } } CloneTypeFoldableAndLiftImpls! { diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs new file mode 100644 index 0000000000000..b1eed05c2a2fd --- /dev/null +++ b/src/librustc/infer/canonical/query_result.rs @@ -0,0 +1,215 @@ +// 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. + +//! This module contains the code to instantiate a "query result", and +//! in particular to extract out the resulting region obligations and +//! encode them therein. +//! +//! For an overview of what canonicaliation is and how it fits into +//! rustc, check out the [chapter in the rustc guide][c]. +//! +//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html + +use infer::canonical::{Canonical, CanonicalVarValues, QueryRegionConstraint, QueryResult}; +use infer::canonical::substitute::substitute_value; +use infer::{InferCtxt, InferOk, InferResult}; +use rustc_data_structures::indexed_vec::Idx; +use std::fmt::Debug; +use traits::{Obligation, ObligationCause, PredicateObligation}; +use ty::fold::TypeFoldable; +use ty::subst::{Kind, UnpackedKind}; +use ty::{self, CanonicalVar}; + +use rustc_data_structures::indexed_vec::IndexVec; + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Given the (canonicalized) result to a canonical query, + /// instantiates the result so it can be used, plugging in the + /// values from the canonical query. (Note that the result may + /// have been ambiguous; you should check the certainty level of + /// the query before applying this function.) + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc guide][c]. + /// + /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#processing-the-canonicalized-query-result + pub fn instantiate_query_result( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &CanonicalVarValues<'tcx>, + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, + ) -> InferResult<'tcx, R> + where + R: Debug + TypeFoldable<'tcx>, + { + debug!( + "instantiate_query_result(original_values={:#?}, query_result={:#?})", + original_values, query_result, + ); + + // Every canonical query result includes values for each of + // the inputs to the query. Therefore, we begin by unifying + // these values with the original inputs that were + // canonicalized. + let result_values = &query_result.value.var_values; + assert_eq!(original_values.len(), result_values.len()); + + // Quickly try to find initial values for the canonical + // variables in the result in terms of the query. We do this + // by iterating down the values that the query gave to each of + // the canonical inputs. If we find that one of those values + // is directly equal to one of the canonical variables in the + // result, then we can type the corresponding value from the + // input. See the example above. + let mut opt_values: IndexVec>> = + IndexVec::from_elem_n(None, query_result.variables.len()); + + // In terms of our example above, we are iterating over pairs like: + // [(?A, Vec), ('static, '?1), (?B, ?0)] + for (original_value, result_value) in original_values.iter().zip(result_values) { + match result_value.unpack() { + UnpackedKind::Type(result_value) => { + // e.g., here `result_value` might be `?0` in the example above... + if let ty::TyInfer(ty::InferTy::CanonicalTy(index)) = result_value.sty { + // in which case we would set `canonical_vars[0]` to `Some(?U)`. + opt_values[index] = Some(original_value); + } + } + UnpackedKind::Lifetime(result_value) => { + // e.g., here `result_value` might be `'?1` in the example above... + if let &ty::RegionKind::ReCanonical(index) = result_value { + // in which case we would set `canonical_vars[0]` to `Some('static)`. + opt_values[index] = Some(original_value); + } + } + } + } + + // Create a result substitution: if we found a value for a + // given variable in the loop above, use that. Otherwise, use + // a fresh inference variable. + let result_subst = &CanonicalVarValues { + var_values: query_result + .variables + .iter() + .enumerate() + .map(|(index, info)| match opt_values[CanonicalVar::new(index)] { + Some(k) => k, + None => self.fresh_inference_var_for_canonical_var(cause.span, *info), + }) + .collect(), + }; + + // Unify the original values for the canonical variables in + // the input with the value found in the query + // post-substitution. Often, but not always, this is a no-op, + // because we already found the mapping in the first step. + let substituted_values = |index: CanonicalVar| -> Kind<'tcx> { + query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index]) + }; + let mut obligations = self + .unify_canonical_vars(cause, param_env, original_values, substituted_values)? + .into_obligations(); + + obligations.extend(self.query_region_constraints_into_obligations( + cause, + param_env, + &query_result.value.region_constraints, + result_subst, + )); + + let user_result: R = + query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value); + + Ok(InferOk { + value: user_result, + obligations, + }) + } + + /// Converts the region constraints resulting from a query into an + /// iterator of obligations. + fn query_region_constraints_into_obligations<'a>( + &'a self, + cause: &'a ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>], + result_subst: &'a CanonicalVarValues<'tcx>, + ) -> impl Iterator> + 'a { + Box::new( + unsubstituted_region_constraints + .iter() + .map(move |constraint| { + let ty::OutlivesPredicate(k1, r2) = constraint.skip_binder(); // restored below + let k1 = substitute_value(self.tcx, result_subst, k1); + let r2 = substitute_value(self.tcx, result_subst, r2); + match k1.unpack() { + UnpackedKind::Lifetime(r1) => Obligation::new( + cause.clone(), + param_env, + ty::Predicate::RegionOutlives(ty::Binder::dummy( + ty::OutlivesPredicate(r1, r2), + )), + ), + + UnpackedKind::Type(t1) => Obligation::new( + cause.clone(), + param_env, + ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate( + t1, r2, + ))), + ), + } + }), + ) as Box> + } + + /// Given two sets of values for the same set of canonical variables, unify them. + /// The second set is produced lazilly by supplying indices from the first set. + fn unify_canonical_vars( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + variables1: &CanonicalVarValues<'tcx>, + variables2: impl Fn(CanonicalVar) -> Kind<'tcx>, + ) -> InferResult<'tcx, ()> { + self.commit_if_ok(|_| { + let mut obligations = vec![]; + for (index, value1) in variables1.var_values.iter_enumerated() { + let value2 = variables2(index); + + match (value1.unpack(), value2.unpack()) { + (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { + obligations + .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); + } + ( + UnpackedKind::Lifetime(ty::ReErased), + UnpackedKind::Lifetime(ty::ReErased), + ) => { + // no action needed + } + (UnpackedKind::Lifetime(v1), UnpackedKind::Lifetime(v2)) => { + obligations + .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); + } + _ => { + bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,); + } + } + } + Ok(InferOk { + value: (), + obligations, + }) + }) + } +} diff --git a/src/librustc/infer/canonical/substitute.rs b/src/librustc/infer/canonical/substitute.rs new file mode 100644 index 0000000000000..5bc1ae689a5d2 --- /dev/null +++ b/src/librustc/infer/canonical/substitute.rs @@ -0,0 +1,113 @@ +// 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. + +//! This module contains code to substitute new values into a +//! `Canonical<'tcx, T>`. +//! +//! For an overview of what canonicaliation is and how it fits into +//! rustc, check out the [chapter in the rustc guide][c]. +//! +//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html + +use infer::canonical::{Canonical, CanonicalVarValues}; +use ty::fold::{TypeFoldable, TypeFolder}; +use ty::subst::UnpackedKind; +use ty::{self, Ty, TyCtxt, TypeFlags}; + +impl<'tcx, V> Canonical<'tcx, V> { + /// Instantiate the wrapped value, replacing each canonical value + /// with the value given in `var_values`. + pub fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V + where + V: TypeFoldable<'tcx>, + { + self.substitute_projected(tcx, var_values, |value| value) + } + + /// Allows one to apply a substitute to some subset of + /// `self.value`. Invoke `projection_fn` with `self.value` to get + /// a value V that is expressed in terms of the same canonical + /// variables bound in `self` (usually this extracts from subset + /// of `self`). Apply the substitution `var_values` to this value + /// V, replacing each of the canonical variables. + pub fn substitute_projected( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + var_values: &CanonicalVarValues<'tcx>, + projection_fn: impl FnOnce(&V) -> &T, + ) -> T + where + T: TypeFoldable<'tcx>, + { + assert_eq!(self.variables.len(), var_values.var_values.len()); + let value = projection_fn(&self.value); + substitute_value(tcx, var_values, value) + } +} + +/// Substitute the values from `var_values` into `value`. `var_values` +/// must be values for the set of canonical variables that appear in +/// `value`. +pub(super) fn substitute_value<'a, 'tcx, T>( + tcx: TyCtxt<'_, '_, 'tcx>, + var_values: &CanonicalVarValues<'tcx>, + value: &'a T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + if var_values.var_values.is_empty() { + debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS)); + value.clone() + } else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) { + value.clone() + } else { + value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values }) + } +} + +struct CanonicalVarValuesSubst<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + var_values: &'cx CanonicalVarValues<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'gcx, 'tcx> { + fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.sty { + ty::TyInfer(ty::InferTy::CanonicalTy(c)) => { + match self.var_values.var_values[c].unpack() { + UnpackedKind::Type(ty) => ty, + r => bug!("{:?} is a type but value is {:?}", c, r), + } + } + _ => { + if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) { + t + } else { + t.super_fold_with(self) + } + } + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match r { + ty::RegionKind::ReCanonical(c) => match self.var_values.var_values[*c].unpack() { + UnpackedKind::Lifetime(l) => l, + r => bug!("{:?} is a region but value is {:?}", c, r), + }, + _ => r.super_fold_with(self), + } + } +} From dfd33f593218aa47030e3d9abc12bf19c3c7525d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 09:29:41 -0400 Subject: [PATCH 22/72] move `make_query_response` into method on infcx --- src/librustc/infer/canonical/query_result.rs | 131 +++++++++++++++++- src/librustc_traits/dropck_outlives.rs | 27 ++-- src/librustc_traits/lib.rs | 1 - .../normalize_projection_ty.rs | 4 +- src/librustc_traits/util.rs | 123 ---------------- 5 files changed, 148 insertions(+), 138 deletions(-) delete mode 100644 src/librustc_traits/util.rs diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index b1eed05c2a2fd..aff9136bad07c 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -17,11 +17,16 @@ //! //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html -use infer::canonical::{Canonical, CanonicalVarValues, QueryRegionConstraint, QueryResult}; use infer::canonical::substitute::substitute_value; +use infer::canonical::{ + Canonical, CanonicalVarValues, Canonicalize, Certainty, QueryRegionConstraint, QueryResult, +}; +use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::{InferCtxt, InferOk, InferResult}; use rustc_data_structures::indexed_vec::Idx; use std::fmt::Debug; +use traits::query::NoSolution; +use traits::{FulfillmentContext, TraitEngine}; use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::fold::TypeFoldable; use ty::subst::{Kind, UnpackedKind}; @@ -29,7 +34,131 @@ use ty::{self, CanonicalVar}; use rustc_data_structures::indexed_vec::IndexVec; +type CanonicalizedQueryResult<'gcx, 'tcx, T> = + as Canonicalize<'gcx, 'tcx>>::Canonicalized; + impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// This method is meant to be invoked as the final step of a canonical query + /// implementation. It is given: + /// + /// - the instantiated variables `inference_vars` created from the query key + /// - the result `answer` of the query + /// - a fulfillment context `fulfill_cx` that may contain various obligations which + /// have yet to be proven. + /// + /// Given this, the function will process the obligations pending + /// in `fulfill_cx`: + /// + /// - If all the obligations can be proven successfully, it will + /// package up any resulting region obligations (extracted from + /// `infcx`) along with the fully resolved value `answer` into a + /// query result (which is then itself canonicalized). + /// - If some obligations can be neither proven nor disproven, then + /// the same thing happens, but the resulting query is marked as ambiguous. + /// - Finally, if any of the obligations result in a hard error, + /// then `Err(NoSolution)` is returned. + pub fn make_canonicalized_query_result( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + fulfill_cx: &mut FulfillmentContext<'tcx>, + ) -> Result, NoSolution> + where + T: Debug, + QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, + { + let tcx = self.tcx; + + debug!( + "make_query_response(\ + inference_vars={:?}, \ + answer={:?})", + inference_vars, answer, + ); + + // Select everything, returning errors. + let true_errors = match fulfill_cx.select_where_possible(self) { + Ok(()) => vec![], + Err(errors) => errors, + }; + debug!("true_errors = {:#?}", true_errors); + + if !true_errors.is_empty() { + // FIXME -- we don't indicate *why* we failed to solve + debug!("make_query_response: true_errors={:#?}", true_errors); + return Err(NoSolution); + } + + // Anything left unselected *now* must be an ambiguity. + let ambig_errors = match fulfill_cx.select_all_or_error(self) { + Ok(()) => vec![], + Err(errors) => errors, + }; + debug!("ambig_errors = {:#?}", ambig_errors); + + let region_obligations = self.take_registered_region_obligations(); + + let region_constraints = self.with_region_constraints(|region_constraints| { + let RegionConstraintData { + constraints, + verifys, + givens, + } = region_constraints; + + assert!(verifys.is_empty()); + assert!(givens.is_empty()); + + let mut outlives: Vec<_> = constraints + .into_iter() + .map(|(k, _)| match *k { + // Swap regions because we are going from sub (<=) to outlives + // (>=). + Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( + tcx.mk_region(ty::ReVar(v2)).into(), + tcx.mk_region(ty::ReVar(v1)), + ), + Constraint::VarSubReg(v1, r2) => { + ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) + } + Constraint::RegSubVar(r1, v2) => { + ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) + } + Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), + }) + .map(ty::Binder::dummy) // no bound regions in the code above + .collect(); + + outlives.extend( + region_obligations + .into_iter() + .map(|(_, r_o)| ty::OutlivesPredicate(r_o.sup_type.into(), r_o.sub_region)) + .map(ty::Binder::dummy), // no bound regions in the code above + ); + + outlives + }); + + let certainty = if ambig_errors.is_empty() { + Certainty::Proven + } else { + Certainty::Ambiguous + }; + + let (canonical_result, _) = self.canonicalize_response(&QueryResult { + var_values: inference_vars, + region_constraints, + certainty, + value: answer, + }); + + debug!( + "make_query_response: canonical_result = {:#?}", + canonical_result + ); + + Ok(canonical_result) + } + /// Given the (canonicalized) result to a canonical query, /// instantiates the result so it can be used, plugging in the /// values from the canonical query. (Note that the result may diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index 219c6b9aefba5..cd49aa2241b3c 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -8,17 +8,16 @@ // 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::infer::canonical::{Canonical, QueryResult}; +use rustc::traits::query::dropck_outlives::{DropckOutlivesResult, DtorckConstraint}; use rustc::traits::query::{CanonicalTyGoal, NoSolution}; -use rustc::traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; -use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; use rustc::ty::subst::{Subst, Substs}; +use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc::util::nodemap::FxHashSet; use rustc_data_structures::sync::Lrc; use syntax::codemap::{Span, DUMMY_SP}; -use util; crate fn dropck_outlives<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, @@ -36,7 +35,10 @@ crate fn dropck_outlives<'tcx>( canonical_inference_vars, ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal); - let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + 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 @@ -135,7 +137,7 @@ crate fn dropck_outlives<'tcx>( debug!("dropck_outlives: result = {:#?}", result); - util::make_query_response(infcx, canonical_inference_vars, result, fulfill_cx) + infcx.make_canonicalized_query_result(canonical_inference_vars, result, fulfill_cx) }) } @@ -184,7 +186,8 @@ fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>( dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety) } - ty::TyTuple(tys) => tys.iter() + ty::TyTuple(tys) => tys + .iter() .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) .collect(), @@ -222,7 +225,10 @@ fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>( dtorck_types: vec![], overflows: vec![], }; - debug!("dtorck_constraint: generator {:?} => {:?}", def_id, constraint); + debug!( + "dtorck_constraint: generator {:?} => {:?}", + def_id, constraint + ); Ok(constraint) } @@ -291,7 +297,8 @@ crate fn adt_dtorck_constraint<'a, 'tcx>( return Ok(result); } - let mut result = def.all_fields() + 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::>()?; diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index c3135439204e7..830aa93c3c3d1 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -33,7 +33,6 @@ mod dropck_outlives; mod evaluate_obligation; mod normalize_projection_ty; mod normalize_erasing_regions; -mod util; pub mod lowering; use rustc::ty::query::Providers; diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index a9ac53972e475..a9c4fef9f7dc1 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -15,7 +15,6 @@ use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::DUMMY_SP; -use util; use std::sync::atomic::Ordering; crate fn normalize_projection_ty<'tcx>( @@ -43,8 +42,7 @@ crate fn normalize_projection_ty<'tcx>( // Now that we have fulfilled as much as we can, create a solution // from what we've learned. - util::make_query_response( - infcx, + infcx.make_canonicalized_query_result( canonical_inference_vars, NormalizationResult { normalized_ty: answer }, fulfill_cx, diff --git a/src/librustc_traits/util.rs b/src/librustc_traits/util.rs deleted file mode 100644 index cdf20bdafadc4..0000000000000 --- a/src/librustc_traits/util.rs +++ /dev/null @@ -1,123 +0,0 @@ -// 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::InferCtxt; -use rustc::infer::canonical::{CanonicalVarValues, Canonicalize, Certainty, QueryResult}; -use rustc::infer::region_constraints::{Constraint, RegionConstraintData}; -use rustc::traits::{FulfillmentContext, TraitEngine}; -use rustc::traits::query::NoSolution; -use rustc::ty; -use std::fmt::Debug; - -/// The canonicalization form of `QueryResult<'tcx, T>`. -type CanonicalizedQueryResult<'gcx, 'tcx, T> = - as Canonicalize<'gcx, 'tcx>>::Canonicalized; - -crate fn make_query_response<'gcx, 'tcx, T>( - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - inference_vars: CanonicalVarValues<'tcx>, - answer: T, - fulfill_cx: &mut FulfillmentContext<'tcx>, -) -> Result, NoSolution> -where - T: Debug, - QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, -{ - let tcx = infcx.tcx; - - debug!( - "make_query_response(\ - inference_vars={:?}, \ - answer={:?})", - inference_vars, answer, - ); - - // Select everything, returning errors. - let true_errors = match fulfill_cx.select_where_possible(infcx) { - Ok(()) => vec![], - Err(errors) => errors, - }; - debug!("true_errors = {:#?}", true_errors); - - if !true_errors.is_empty() { - // FIXME -- we don't indicate *why* we failed to solve - debug!("make_query_response: true_errors={:#?}", true_errors); - return Err(NoSolution); - } - - // Anything left unselected *now* must be an ambiguity. - let ambig_errors = match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => vec![], - Err(errors) => errors, - }; - debug!("ambig_errors = {:#?}", ambig_errors); - - let region_obligations = infcx.take_registered_region_obligations(); - - let region_constraints = infcx.with_region_constraints(|region_constraints| { - let RegionConstraintData { - constraints, - verifys, - givens, - } = region_constraints; - - assert!(verifys.is_empty()); - assert!(givens.is_empty()); - - let mut outlives: Vec<_> = constraints - .into_iter() - .map(|(k, _)| match *k { - // Swap regions because we are going from sub (<=) to outlives - // (>=). - Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( - tcx.mk_region(ty::ReVar(v2)).into(), - tcx.mk_region(ty::ReVar(v1)), - ), - Constraint::VarSubReg(v1, r2) => { - ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) - } - Constraint::RegSubVar(r1, v2) => { - ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) - } - Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), - }) - .map(ty::Binder::dummy) // no bound regions in the code above - .collect(); - - outlives.extend( - region_obligations - .into_iter() - .map(|(_, r_o)| ty::OutlivesPredicate(r_o.sup_type.into(), r_o.sub_region)) - .map(ty::Binder::dummy) // no bound regions in the code above - ); - - outlives - }); - - let certainty = if ambig_errors.is_empty() { - Certainty::Proven - } else { - Certainty::Ambiguous - }; - - let (canonical_result, _) = infcx.canonicalize_response(&QueryResult { - var_values: inference_vars, - region_constraints, - certainty, - value: answer, - }); - - debug!( - "make_query_response: canonical_result = {:#?}", - canonical_result - ); - - Ok(canonical_result) -} From bebd3ff6666d3e3f9bf458c5fab6c71c038db54f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 09:34:31 -0400 Subject: [PATCH 23/72] extract a helper for `make_query_result` that skips canonicalization --- src/librustc/infer/canonical/query_result.rs | 38 ++++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index aff9136bad07c..190a6c18ff840 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -63,6 +63,29 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { answer: T, fulfill_cx: &mut FulfillmentContext<'tcx>, ) -> Result, NoSolution> + where + T: Debug, + QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, + { + let query_result = self.make_query_result(inference_vars, answer, fulfill_cx)?; + let (canonical_result, _) = self.canonicalize_response(&query_result); + + debug!( + "make_canonicalized_query_result: canonical_result = {:#?}", + canonical_result + ); + + Ok(canonical_result) + } + + /// Helper for `make_canonicalized_query_result` that does + /// everything up until the final canonicalization. + fn make_query_result( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + fulfill_cx: &mut FulfillmentContext<'tcx>, + ) -> Result, NoSolution> where T: Debug, QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, @@ -70,7 +93,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { let tcx = self.tcx; debug!( - "make_query_response(\ + "make_query_result(\ inference_vars={:?}, \ answer={:?})", inference_vars, answer, @@ -85,7 +108,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { if !true_errors.is_empty() { // FIXME -- we don't indicate *why* we failed to solve - debug!("make_query_response: true_errors={:#?}", true_errors); + debug!("make_query_result: true_errors={:#?}", true_errors); return Err(NoSolution); } @@ -144,19 +167,12 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { Certainty::Ambiguous }; - let (canonical_result, _) = self.canonicalize_response(&QueryResult { + Ok(QueryResult { var_values: inference_vars, region_constraints, certainty, value: answer, - }); - - debug!( - "make_query_response: canonical_result = {:#?}", - canonical_result - ); - - Ok(canonical_result) + }) } /// Given the (canonicalized) result to a canonical query, From 2624c14a26bdab3af7c9a6d03c30d6b913cb77f5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 09:52:51 -0400 Subject: [PATCH 24/72] extract the handling of region constraints from queries --- src/librustc/infer/canonical/query_result.rs | 90 +++++++++++--------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 190a6c18ff840..88d5d10d3c2b9 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -22,15 +22,16 @@ use infer::canonical::{ Canonical, CanonicalVarValues, Canonicalize, Certainty, QueryRegionConstraint, QueryResult, }; use infer::region_constraints::{Constraint, RegionConstraintData}; -use infer::{InferCtxt, InferOk, InferResult}; +use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; use rustc_data_structures::indexed_vec::Idx; use std::fmt::Debug; +use syntax::ast; use traits::query::NoSolution; use traits::{FulfillmentContext, TraitEngine}; use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::fold::TypeFoldable; use ty::subst::{Kind, UnpackedKind}; -use ty::{self, CanonicalVar}; +use ty::{self, CanonicalVar, TyCtxt}; use rustc_data_structures::indexed_vec::IndexVec; @@ -120,45 +121,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { debug!("ambig_errors = {:#?}", ambig_errors); let region_obligations = self.take_registered_region_obligations(); - let region_constraints = self.with_region_constraints(|region_constraints| { - let RegionConstraintData { - constraints, - verifys, - givens, - } = region_constraints; - - assert!(verifys.is_empty()); - assert!(givens.is_empty()); - - let mut outlives: Vec<_> = constraints - .into_iter() - .map(|(k, _)| match *k { - // Swap regions because we are going from sub (<=) to outlives - // (>=). - Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( - tcx.mk_region(ty::ReVar(v2)).into(), - tcx.mk_region(ty::ReVar(v1)), - ), - Constraint::VarSubReg(v1, r2) => { - ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) - } - Constraint::RegSubVar(r1, v2) => { - ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) - } - Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), - }) - .map(ty::Binder::dummy) // no bound regions in the code above - .collect(); - - outlives.extend( - region_obligations - .into_iter() - .map(|(_, r_o)| ty::OutlivesPredicate(r_o.sup_type.into(), r_o.sub_region)) - .map(ty::Binder::dummy), // no bound regions in the code above - ); - - outlives + make_query_outlives(tcx, region_obligations, region_constraints) }); let certainty = if ambig_errors.is_empty() { @@ -358,3 +322,49 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { }) } } + +/// Given the region obligations and constraints scraped from the infcx, +/// creates query region constraints. +fn make_query_outlives<'tcx>( + tcx: TyCtxt<'_, '_, 'tcx>, + region_obligations: Vec<(ast::NodeId, RegionObligation<'tcx>)>, + region_constraints: &RegionConstraintData<'tcx>, +) -> Vec> { + let RegionConstraintData { + constraints, + verifys, + givens, + } = region_constraints; + + assert!(verifys.is_empty()); + assert!(givens.is_empty()); + + let mut outlives: Vec<_> = constraints + .into_iter() + .map(|(k, _)| match *k { + // Swap regions because we are going from sub (<=) to outlives + // (>=). + Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( + tcx.mk_region(ty::ReVar(v2)).into(), + tcx.mk_region(ty::ReVar(v1)), + ), + Constraint::VarSubReg(v1, r2) => { + ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) + } + Constraint::RegSubVar(r1, v2) => { + ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) + } + Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), + }) + .map(ty::Binder::dummy) // no bound regions in the code above + .collect(); + + outlives.extend( + region_obligations + .into_iter() + .map(|(_, r_o)| ty::OutlivesPredicate(r_o.sup_type.into(), r_o.sub_region)) + .map(ty::Binder::dummy), // no bound regions in the code above + ); + + outlives +} From c8cf710ce032e87947d0107dcfb5d3f4c90a6849 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 10:08:44 -0400 Subject: [PATCH 25/72] replace `LexicalRegionConstraintData` with `QueryRegionConstraint` --- src/librustc/infer/canonical/mod.rs | 2 +- src/librustc/infer/canonical/query_result.rs | 2 +- .../nll/type_check/constraint_conversion.rs | 134 +++++++++--------- .../borrow_check/nll/type_check/liveness.rs | 5 +- .../borrow_check/nll/type_check/mod.rs | 29 +--- .../nll/type_check/type_op/mod.rs | 37 ++--- 6 files changed, 86 insertions(+), 123 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 037fc637ec91e..33e5f6cd3e271 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -44,7 +44,7 @@ use rustc_data_structures::indexed_vec::IndexVec; mod canonicalizer; -mod query_result; +pub mod query_result; mod substitute; diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 88d5d10d3c2b9..4d03ccb42a9f6 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -325,7 +325,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Given the region obligations and constraints scraped from the infcx, /// creates query region constraints. -fn make_query_outlives<'tcx>( +pub fn make_query_outlives<'tcx>( tcx: TyCtxt<'_, '_, 'tcx>, region_obligations: Vec<(ast::NodeId, RegionObligation<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 44f8420ca7ed0..96e99aae69373 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -11,13 +11,15 @@ use borrow_check::location::LocationTable; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::{OutlivesConstraint, RegionTest, TypeTest}; -use borrow_check::nll::type_check::{Locations, LexicalRegionConstraintData}; +use borrow_check::nll::type_check::Locations; use borrow_check::nll::universal_regions::UniversalRegions; -use rustc::infer::{self, RegionObligation, SubregionOrigin}; +use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; -use rustc::infer::region_constraints::{Constraint, GenericKind, VerifyBound}; +use rustc::infer::region_constraints::{GenericKind, VerifyBound}; +use rustc::infer::{self, SubregionOrigin}; use rustc::mir::{Location, Mir}; use rustc::ty::{self, TyCtxt}; +use rustc::ty::subst::UnpackedKind; use syntax::codemap::Span; crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { @@ -63,49 +65,10 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } } - pub(super) fn convert(&mut self, data: &LexicalRegionConstraintData<'tcx>) { + pub(super) fn convert(&mut self, query_constraints: &[QueryRegionConstraint<'tcx>]) { debug!("generate: constraints at: {:#?}", self.locations); - let LexicalRegionConstraintData { - constraints, - region_obligations, - } = data; - - for constraint in constraints { - debug!("generate: constraint: {:?}", constraint); - let (a_vid, b_vid) = match constraint { - Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid), - Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid), - Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)), - Constraint::RegSubReg(a_r, b_r) => { - (self.to_region_vid(a_r), self.to_region_vid(b_r)) - } - }; - - // We have the constraint that `a_vid <= b_vid`. Add - // `b_vid: a_vid` to our region checker. Note that we - // reverse direction, because `regioncx` talks about - // "outlives" (`>=`) whereas the region constraints - // talk about `<=`. - self.add_outlives(b_vid, a_vid); - - // In the new analysis, all outlives relations etc - // "take effect" at the mid point of the statement - // that requires them, so ignore the `at_location`. - if let Some(all_facts) = &mut self.all_facts { - if let Some(from_location) = self.locations.from_location() { - all_facts.outlives.push(( - b_vid, - a_vid, - self.location_table.mid_index(from_location), - )); - } else { - for location in self.location_table.all_points() { - all_facts.outlives.push((b_vid, a_vid, location)); - } - } - } - } + // Extract out various useful fields we'll need below. let ConstraintConversion { tcx, region_bound_pairs, @@ -113,23 +76,59 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { param_env, .. } = *self; - for r_o in region_obligations { - let RegionObligation { - sup_type, - sub_region, - cause, - } = r_o; - - // we don't actually use this for anything. - let origin = infer::RelateParamBound(cause.span, sup_type); - - TypeOutlives::new( - &mut *self, - tcx, - region_bound_pairs, - implicit_region_bound, - param_env, - ).type_must_outlive(origin, sup_type, sub_region); + + for query_constraint in query_constraints { + // At the moment, we never generate any "higher-ranked" + // region constraints like `for<'a> 'a: 'b`. At some point + // when we move to universes, we will, and this assertion + // will start to fail. + let ty::OutlivesPredicate(k1, r2) = + query_constraint.no_late_bound_regions().unwrap_or_else(|| { + span_bug!( + self.span(), + "query_constraint {:?} contained bound regions", + query_constraint, + ); + }); + + match k1.unpack() { + UnpackedKind::Lifetime(r1) => { + let r1_vid = self.to_region_vid(r1); + let r2_vid = self.to_region_vid(r2); + self.add_outlives(r1_vid, r2_vid); + + // In the new analysis, all outlives relations etc + // "take effect" at the mid point of the statement + // that requires them, so ignore the `at_location`. + if let Some(all_facts) = &mut self.all_facts { + if let Some(from_location) = self.locations.from_location() { + all_facts.outlives.push(( + r1_vid, + r2_vid, + self.location_table.mid_index(from_location), + )); + } else { + for location in self.location_table.all_points() { + all_facts.outlives.push((r1_vid, r2_vid, location)); + } + } + } + } + + UnpackedKind::Type(t1) => { + // we don't actually use this for anything, but + // the `TypeOutlives` code needs an origin. + let origin = infer::RelateParamBound(self.span(), t1); + + TypeOutlives::new( + &mut *self, + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ).type_must_outlive(origin, t1, r2); + } + } } } @@ -185,17 +184,12 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } fn span(&self) -> Span { - self - .mir + self.mir .source_info(self.locations.from_location().unwrap_or(Location::START)) .span } - fn add_outlives( - &mut self, - sup: ty::RegionVid, - sub: ty::RegionVid, - ) { + fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) { let span = self.span(); let point = self.locations.at_location().unwrap_or(Location::START); @@ -213,7 +207,9 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } } -impl<'a, 'b, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'gcx, 'tcx> { +impl<'a, 'b, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> + for &'a mut ConstraintConversion<'b, 'gcx, 'tcx> +{ fn push_sub_region_constraint( &mut self, _origin: SubregionOrigin<'tcx>, 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 92a60602b796a..37898adeb923e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -10,10 +10,11 @@ use borrow_check::nll::region_infer::Cause; use borrow_check::nll::type_check::type_op::{DropckOutlives, TypeOp}; -use borrow_check::nll::type_check::{AtLocation, LexicalRegionConstraintData}; +use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; +use rustc::infer::canonical::QueryRegionConstraint; use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::ty::subst::Kind; @@ -70,7 +71,7 @@ where struct DropData<'tcx> { dropped_kinds: Vec>, - region_constraint_data: Option>>, + region_constraint_data: Option>>>, } impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index cb16461de4914..92ee1f2a892ef 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -20,8 +20,9 @@ use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; use rustc::hir::def_id::DefId; -use rustc::infer::region_constraints::{Constraint, GenericKind}; -use rustc::infer::{InferCtxt, LateBoundRegionConversionTime, RegionObligation, UnitResult}; +use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::region_constraints::GenericKind; +use rustc::infer::{InferCtxt, LateBoundRegionConversionTime, UnitResult}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; @@ -625,21 +626,6 @@ crate struct MirTypeckRegionConstraints<'tcx> { crate type_tests: Vec>, } -/// The type checker layers on top of the "old" inference engine. The -/// idea is that we run some operations, like trait selection, and -/// then we "scrape out" the region constraints that have accumulated -/// from the old lexical solver. This struct just collects the bits of -/// that data that we care about into one place. -#[derive(Debug)] -struct LexicalRegionConstraintData<'tcx> { - /// The `'a <= 'b` constraints extracted from `RegionConstraintData`. - constraints: Vec>, - - /// The `T: 'a` (and `'a: 'b`, in some cases) constraints - /// extracted from the pending "region obligations". - region_obligations: Vec>, -} - /// The `Locations` type summarizes *where* region constraints are /// required to hold. Normally, this is at a particular point which /// created the obligation, but for constraints that the user gave, we @@ -759,7 +745,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn push_region_constraints( &mut self, locations: Locations, - data: &LexicalRegionConstraintData<'tcx>, + data: &[QueryRegionConstraint<'tcx>], ) { debug!( "push_region_constraints: constraints generated at {:?} are {:#?}", @@ -1703,10 +1689,3 @@ impl ToLocations for Location { self.at_self() } } - -impl<'tcx> LexicalRegionConstraintData<'tcx> { - fn is_empty(&self) -> bool { - let LexicalRegionConstraintData { constraints, region_obligations } = self; - constraints.is_empty() && region_obligations.is_empty() - } -} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index 263bce8067f2d..434b1d799ae16 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::type_check::LexicalRegionConstraintData; -use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::canonical::query_result; +use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::{InferCtxt, InferOk, InferResult}; use rustc::traits::query::NoSolution; use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation, TraitEngine}; @@ -44,7 +44,7 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Result<(Self::Output, Option>>), TypeError<'tcx>> { + ) -> Result<(Self::Output, Option>>>), TypeError<'tcx>> { let op = match self.trivial_noop() { Ok(r) => return Ok((r, None)), Err(op) => op, @@ -66,33 +66,20 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { ); } - let region_obligations: Vec<_> = infcx - .take_registered_region_obligations() - .into_iter() - .map(|(_node_id, region_obligation)| region_obligation) - .collect(); + let region_obligations = infcx.take_registered_region_obligations(); - let RegionConstraintData { - constraints, - verifys, - givens, - } = infcx.take_and_reset_region_constraints(); + let region_constraint_data = infcx.take_and_reset_region_constraints(); - // These are created when we "process" the registered region - // obliations, and that hasn't happened yet. - assert!(verifys.is_empty()); - - // NLL doesn't use givens (and thank goodness!). - assert!(givens.is_empty()); - - let data = LexicalRegionConstraintData { - constraints: constraints.keys().cloned().collect(), + let outlives = query_result::make_query_outlives( + infcx.tcx, region_obligations, - }; - if data.is_empty() { + ®ion_constraint_data, + ); + + if outlives.is_empty() { Ok((value, None)) } else { - Ok((value, Some(Rc::new(data)))) + Ok((value, Some(Rc::new(outlives)))) } } } From 7358931a1c7ab6b81b702496ce4bb0c9dac6a70d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 16:22:04 -0400 Subject: [PATCH 26/72] improve `trivial_case` handling --- src/librustc/traits/query/dropck_outlives.rs | 2 +- .../nll/type_check/type_op/mod.rs | 45 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index af1d2c77c28a8..96bed1223502a 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -210,7 +210,7 @@ impl_stable_hash_for!(struct DtorckConstraint<'tcx> { /// /// Note also that `needs_drop` requires a "global" type (i.e., one /// with erased regions), but this funtcion does not. -fn trivial_dropck_outlives<'cx, 'tcx>(tcx: TyCtxt<'cx, '_, 'tcx>, ty: Ty<'tcx>) -> bool { +pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.sty { // None of these types have a destructor and hence they do not // require anything in particular to outlive the dtor's diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index 434b1d799ae16..6ba1a70f6126a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -11,12 +11,13 @@ use rustc::infer::canonical::query_result; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::{InferCtxt, InferOk, InferResult}; +use rustc::traits::query::dropck_outlives::trivial_dropck_outlives; use rustc::traits::query::NoSolution; use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation, TraitEngine}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Kind; -use rustc::ty::{ParamEnv, Predicate, Ty}; +use rustc::ty::{ParamEnv, Predicate, Ty, TyCtxt}; use std::fmt; use std::rc::Rc; use syntax::codemap::DUMMY_SP; @@ -26,7 +27,7 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { /// Micro-optimization: returns `Ok(x)` if we can trivially /// produce the output, else returns `Err(self)` back. - fn trivial_noop(self) -> Result; + fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; /// Given an infcx, performs **the kernel** of the operation: this does the /// key action and then, optionally, returns a set of obligations which must be proven. @@ -40,29 +41,35 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). - #[inline(never)] fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, ) -> Result<(Self::Output, Option>>>), TypeError<'tcx>> { - let op = match self.trivial_noop() { - Ok(r) => return Ok((r, None)), - Err(op) => op, - }; + match self.trivial_noop(infcx.tcx) { + Ok(r) => Ok((r, None)), + Err(op) => op.fully_perform_nontrivial(infcx), + } + } + /// Helper for `fully_perform` that handles the nontrivial cases. + #[inline(never)] // just to help with profiling + fn fully_perform_nontrivial( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ) -> Result<(Self::Output, Option>>>), TypeError<'tcx>> { if cfg!(debug_assertions) { - info!("fully_perform_op_and_get_region_constraint_data({:?})", op); + info!("fully_perform_op_and_get_region_constraint_data({:?})", self); } let mut fulfill_cx = TraitEngine::new(infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; - let InferOk { value, obligations } = infcx.commit_if_ok(|_| op.perform(infcx))?; + let InferOk { value, obligations } = infcx.commit_if_ok(|_| self.perform(infcx))?; debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); fulfill_cx.register_predicate_obligations(infcx, obligations); if let Err(e) = fulfill_cx.select_all_or_error(infcx) { infcx.tcx.sess.diagnostic().delay_span_bug( DUMMY_SP, - &format!("errors selecting obligation during MIR typeck: {:?}", e) + &format!("errors selecting obligation during MIR typeck: {:?}", e), ); } @@ -109,7 +116,7 @@ where { type Output = R; - fn trivial_noop(self) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { Err(self) } @@ -147,7 +154,7 @@ impl<'tcx> Subtype<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { type Output = (); - fn trivial_noop(self) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if self.sub == self.sup { Ok(()) } else { @@ -178,7 +185,7 @@ impl<'tcx> Eq<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { type Output = (); - fn trivial_noop(self) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if self.a == self.b { Ok(()) } else { @@ -215,7 +222,7 @@ impl<'tcx> ProvePredicates<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { type Output = (); - fn trivial_noop(self) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if self.obligations.is_empty() { Ok(()) } else { @@ -252,7 +259,7 @@ where { type Output = T; - fn trivial_noop(self) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if !self.value.has_projections() { Ok(self.value) } else { @@ -289,8 +296,12 @@ impl<'tcx> DropckOutlives<'tcx> { impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { type Output = Vec>; - fn trivial_noop(self) -> Result { - Err(self) + fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + if trivial_dropck_outlives(tcx, self.dropped_ty) { + Ok(vec![]) + } else { + Err(self) + } } fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { From be27a5a7756b25c958bc37932b3851a3cb38ed9e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 16:58:12 -0400 Subject: [PATCH 27/72] make one `Canonicalize` impl for `QueryResult` This lets us simplify a few type aliases. --- src/librustc/infer/canonical/mod.rs | 39 ++++++++++++++------ src/librustc/infer/canonical/query_result.rs | 17 ++++----- src/librustc/traits/query/dropck_outlives.rs | 15 +------- src/librustc/traits/query/normalize.rs | 15 +------- 4 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 33e5f6cd3e271..f60ae20827a52 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -32,15 +32,15 @@ //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html use infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; +use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::sync::Lrc; use serialize::UseSpecializedDecodable; use std::fmt::Debug; use std::ops::Index; use syntax::codemap::Span; -use ty::{self, CanonicalVar, Lift, Region, Slice, TyCtxt}; -use ty::subst::Kind; use ty::fold::TypeFoldable; - -use rustc_data_structures::indexed_vec::IndexVec; +use ty::subst::Kind; +use ty::{self, CanonicalVar, Lift, Region, Slice, TyCtxt}; mod canonicalizer; @@ -59,7 +59,7 @@ pub struct Canonical<'gcx, V> { pub type CanonicalVarInfos<'gcx> = &'gcx Slice; -impl<'gcx> UseSpecializedDecodable for CanonicalVarInfos<'gcx> { } +impl<'gcx> UseSpecializedDecodable for CanonicalVarInfos<'gcx> {} /// A set of values corresponding to the canonical variables from some /// `Canonical`. You can give these values to @@ -124,6 +124,9 @@ pub struct QueryResult<'tcx, R> { pub value: R, } +pub type CanonicalizedQueryResult<'gcx, T> = + Lrc>::Lifted>>>; + /// Indicates whether or not we were able to prove the query to be /// true. #[derive(Copy, Clone, Debug)] @@ -246,9 +249,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { CanonicalVarKind::Ty(ty_kind) => { let ty = match ty_kind { CanonicalTyVarKind::General => { - self.next_ty_var( - TypeVariableOrigin::MiscVariable(span), - ) + self.next_ty_var(TypeVariableOrigin::MiscVariable(span)) } CanonicalTyVarKind::Int => self.tcx.mk_int_var(self.next_int_var_id()), @@ -258,9 +259,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ty.into() } - CanonicalVarKind::Region => { - self.next_region_var(RegionVariableOrigin::MiscVariable(span)).into() - } + CanonicalVarKind::Region => self + .next_region_var(RegionVariableOrigin::MiscVariable(span)) + .into(), } } } @@ -343,3 +344,19 @@ impl<'tcx> Index for CanonicalVarValues<'tcx> { &self.var_values[value] } } + +impl<'gcx: 'tcx, 'tcx, T> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, T> +where + T: TypeFoldable<'tcx> + Lift<'gcx>, + T::Lifted: Debug, +{ + // we ought to intern this, but I'm too lazy just now + type Canonicalized = Lrc>>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + Lrc::new(value) + } +} diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 4d03ccb42a9f6..ab8fb519afd98 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -19,11 +19,13 @@ use infer::canonical::substitute::substitute_value; use infer::canonical::{ - Canonical, CanonicalVarValues, Canonicalize, Certainty, QueryRegionConstraint, QueryResult, + Canonical, CanonicalVarValues, Canonicalize, CanonicalizedQueryResult, Certainty, + QueryRegionConstraint, QueryResult, }; use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::indexed_vec::IndexVec; use std::fmt::Debug; use syntax::ast; use traits::query::NoSolution; @@ -31,12 +33,7 @@ use traits::{FulfillmentContext, TraitEngine}; use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::fold::TypeFoldable; use ty::subst::{Kind, UnpackedKind}; -use ty::{self, CanonicalVar, TyCtxt}; - -use rustc_data_structures::indexed_vec::IndexVec; - -type CanonicalizedQueryResult<'gcx, 'tcx, T> = - as Canonicalize<'gcx, 'tcx>>::Canonicalized; +use ty::{self, CanonicalVar, Lift, TyCtxt}; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// This method is meant to be invoked as the final step of a canonical query @@ -63,10 +60,10 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { inference_vars: CanonicalVarValues<'tcx>, answer: T, fulfill_cx: &mut FulfillmentContext<'tcx>, - ) -> Result, NoSolution> + ) -> Result, NoSolution> where - T: Debug, - QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, + T: Debug + Lift<'gcx> + TypeFoldable<'tcx>, + T::Lifted: Debug, { let query_result = self.make_query_result(inference_vars, answer, fulfill_cx)?; let (canonical_result, _) = self.canonicalize_response(&query_result); diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 96bed1223502a..671809c059ad8 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -9,13 +9,12 @@ // except according to those terms. use infer::at::At; -use infer::canonical::{Canonical, Canonicalize, QueryResult}; +use infer::canonical::{Canonical, Canonicalize}; use infer::InferOk; use std::iter::FromIterator; use traits::query::CanonicalTyGoal; use ty::{self, Ty, TyCtxt}; use ty::subst::Kind; -use rustc_data_structures::sync::Lrc; impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { /// Given a type `ty` of some value being dropped, computes a set @@ -181,18 +180,6 @@ 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 = Lrc>>>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - Lrc::new(value) - } -} - impl_stable_hash_for!(struct DtorckConstraint<'tcx> { outlives, dtorck_types, diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index d0ae0bdac8c09..22b47458bcecc 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -14,10 +14,9 @@ use infer::{InferCtxt, InferOk}; use infer::at::At; -use infer::canonical::{Canonical, Canonicalize, QueryResult}; +use infer::canonical::{Canonical, Canonicalize}; use middle::const_val::ConstVal; use mir::interpret::GlobalId; -use rustc_data_structures::sync::Lrc; use traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use traits::query::CanonicalProjectionGoal; use traits::project::Normalized; @@ -262,18 +261,6 @@ impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, ty::Pr } } -impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, NormalizationResult<'tcx>> { - // we ought to intern this, but I'm too lazy just now - type Canonicalized = Lrc>>>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - Lrc::new(value) - } -} - impl_stable_hash_for!(struct NormalizationResult<'tcx> { normalized_ty }); From 21592cdecd527b329cf4e135609433fc23e6ef84 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 17:38:07 -0400 Subject: [PATCH 28/72] move `Debug` bounds onto `Lift` Just for convenience. --- src/librustc/infer/canonical/mod.rs | 1 - src/librustc/infer/canonical/query_result.rs | 1 - src/librustc/ty/context.rs | 5 +++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index f60ae20827a52..6af8fee663159 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -348,7 +348,6 @@ impl<'tcx> Index for CanonicalVarValues<'tcx> { impl<'gcx: 'tcx, 'tcx, T> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, T> where T: TypeFoldable<'tcx> + Lift<'gcx>, - T::Lifted: Debug, { // we ought to intern this, but I'm too lazy just now type Canonicalized = Lrc>>; diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index ab8fb519afd98..d61434daf9934 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -63,7 +63,6 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) -> Result, NoSolution> where T: Debug + Lift<'gcx> + TypeFoldable<'tcx>, - T::Lifted: Debug, { let query_result = self.make_query_result(inference_vars, answer, fulfill_cx)?; let (canonical_result, _) = self.canonicalize_response(&query_result); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6cbf4fad02cb9..111167cfc1090 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -64,6 +64,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::hash_map::{self, Entry}; use std::hash::{Hash, Hasher}; +use std::fmt::Debug; use std::mem; use std::ops::Deref; use std::iter; @@ -1503,8 +1504,8 @@ impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> { /// contain the TypeVariants key or if the address of the interned /// pointer differs. The latter case is possible if a primitive type, /// e.g. `()` or `u8`, was interned in a different context. -pub trait Lift<'tcx> { - type Lifted: 'tcx; +pub trait Lift<'tcx>: Debug { + type Lifted: Debug + 'tcx; fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option; } From 7cb86ed1e360be8a978e6e4c891c01bc1d8deaaa Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 20:56:12 -0400 Subject: [PATCH 29/72] change to `crate` privacy instead of `pub(super)` --- .../nll/type_check/type_op/mod.rs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index 6ba1a70f6126a..da8d17767af61 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -22,7 +22,7 @@ use std::fmt; use std::rc::Rc; use syntax::codemap::DUMMY_SP; -pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { +crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; /// Micro-optimization: returns `Ok(x)` if we can trivially @@ -91,7 +91,7 @@ pub(super) trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { } } -pub(super) struct CustomTypeOp { +crate struct CustomTypeOp { closure: F, description: G, } @@ -135,14 +135,14 @@ where } #[derive(Debug)] -pub(super) struct Subtype<'tcx> { +crate struct Subtype<'tcx> { param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>, } impl<'tcx> Subtype<'tcx> { - pub(super) fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + crate fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { Self { param_env, sub, @@ -170,14 +170,14 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { } #[derive(Debug)] -pub(super) struct Eq<'tcx> { +crate struct Eq<'tcx> { param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, } impl<'tcx> Eq<'tcx> { - pub(super) fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + crate fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { Self { param_env, a, b } } } @@ -201,12 +201,12 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { } #[derive(Debug)] -pub(super) struct ProvePredicates<'tcx> { +crate struct ProvePredicates<'tcx> { obligations: Vec>, } impl<'tcx> ProvePredicates<'tcx> { - pub(super) fn new( + crate fn new( param_env: ParamEnv<'tcx>, predicates: impl IntoIterator>, ) -> Self { @@ -239,7 +239,7 @@ impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { } #[derive(Debug)] -pub(super) struct Normalize<'tcx, T> { +crate struct Normalize<'tcx, T> { param_env: ParamEnv<'tcx>, value: T, } @@ -248,7 +248,7 @@ impl<'tcx, T> Normalize<'tcx, T> where T: fmt::Debug + TypeFoldable<'tcx>, { - pub(super) fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { + crate fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { Self { param_env, value } } } @@ -279,13 +279,13 @@ where } #[derive(Debug)] -pub(super) struct DropckOutlives<'tcx> { +crate struct DropckOutlives<'tcx> { param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>, } impl<'tcx> DropckOutlives<'tcx> { - pub(super) fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { + crate fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { DropckOutlives { param_env, dropped_ty, From 3f1961d62e166cfe71273175296d9d59222ca81c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Jun 2018 21:04:03 -0400 Subject: [PATCH 30/72] extract type-ops into their own submodules --- .../nll/type_check/input_output.rs | 2 +- .../borrow_check/nll/type_check/liveness.rs | 3 +- .../borrow_check/nll/type_check/mod.rs | 8 +- .../nll/type_check/type_op/custom.rs | 56 ++++ .../borrow_check/nll/type_check/type_op/eq.rs | 44 ++++ .../nll/type_check/type_op/mod.rs | 240 +----------------- .../nll/type_check/type_op/normalize.rs | 56 ++++ .../nll/type_check/type_op/outlives.rs | 48 ++++ .../nll/type_check/type_op/predicates.rs | 51 ++++ .../nll/type_check/type_op/subtype.rs | 48 ++++ 10 files changed, 323 insertions(+), 233 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs create mode 100644 src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index 8144641a5efbf..a127128818e15 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -18,7 +18,7 @@ //! contain revealed `impl Trait` values). use borrow_check::nll::renumber; -use borrow_check::nll::type_check::type_op::CustomTypeOp; +use borrow_check::nll::type_check::type_op::custom::CustomTypeOp; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferOk; 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 37898adeb923e..780269930913d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -9,7 +9,8 @@ // except according to those terms. use borrow_check::nll::region_infer::Cause; -use borrow_check::nll::type_check::type_op::{DropckOutlives, TypeOp}; +use borrow_check::nll::type_check::type_op::TypeOp; +use borrow_check::nll::type_check::type_op::outlives::DropckOutlives; use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 92ee1f2a892ef..d7812d86574fd 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -776,12 +776,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { locations: Locations, ) -> UnitResult<'tcx> { let param_env = self.param_env; - self.fully_perform_op(locations, type_op::Subtype::new(param_env, sub, sup)) + self.fully_perform_op(locations, type_op::subtype::Subtype::new(param_env, sub, sup)) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { let param_env = self.param_env; - self.fully_perform_op(locations, type_op::Eq::new(param_env, b, a)) + self.fully_perform_op(locations, type_op::eq::Eq::new(param_env, b, a)) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { @@ -1560,7 +1560,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.fully_perform_op( location.at_self(), - type_op::ProvePredicates::new(param_env, predicates), + type_op::predicates::ProvePredicates::new(param_env, predicates), ).unwrap() } @@ -1598,7 +1598,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.fully_perform_op( location.to_locations(), - type_op::Normalize::new(param_env, value), + type_op::normalize::Normalize::new(param_env, value), ).unwrap() } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs new file mode 100644 index 0000000000000..ce17cab8dcb09 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs @@ -0,0 +1,56 @@ +// Copyright 2016 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::{InferCtxt, InferResult}; +use rustc::ty::TyCtxt; +use std::fmt; + +crate struct CustomTypeOp { + closure: F, + description: G, +} + +impl CustomTypeOp { + crate fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self + where + F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + G: Fn() -> String, + { + CustomTypeOp { + closure, + description, + } + } +} + +impl<'gcx, 'tcx, F, R, G> super::TypeOp<'gcx, 'tcx> for CustomTypeOp +where + F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + G: Fn() -> String, +{ + type Output = R; + + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + Err(self) + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { + (self.closure)(infcx) + } +} + +impl fmt::Debug for CustomTypeOp +where + G: Fn() -> String, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", (self.description)()) + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs new file mode 100644 index 0000000000000..b062eff0733c2 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs @@ -0,0 +1,44 @@ +// Copyright 2016 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::{InferCtxt, InferResult}; +use rustc::traits::ObligationCause; +use rustc::ty::{ParamEnv, Ty, TyCtxt}; + +#[derive(Debug)] +crate struct Eq<'tcx> { + param_env: ParamEnv<'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, +} + +impl<'tcx> Eq<'tcx> { + crate fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + Self { param_env, a, b } + } +} + +impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for Eq<'tcx> { + type Output = (); + + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + if self.a == self.b { + Ok(()) + } else { + Err(self) + } + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + infcx + .at(&ObligationCause::dummy(), self.param_env) + .eq(self.a, self.b) + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index da8d17767af61..448dfd853c1b3 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -11,17 +11,20 @@ use rustc::infer::canonical::query_result; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::{InferCtxt, InferOk, InferResult}; -use rustc::traits::query::dropck_outlives::trivial_dropck_outlives; -use rustc::traits::query::NoSolution; -use rustc::traits::{Normalized, Obligation, ObligationCause, PredicateObligation, TraitEngine}; +use rustc::traits::{ObligationCause, TraitEngine}; use rustc::ty::error::TypeError; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::Kind; -use rustc::ty::{ParamEnv, Predicate, Ty, TyCtxt}; +use rustc::ty::TyCtxt; use std::fmt; use std::rc::Rc; use syntax::codemap::DUMMY_SP; +crate mod custom; +crate mod eq; +crate mod normalize; +crate mod predicates; +crate mod outlives; +crate mod subtype; + crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; @@ -58,7 +61,10 @@ crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { infcx: &InferCtxt<'_, 'gcx, 'tcx>, ) -> Result<(Self::Output, Option>>>), TypeError<'tcx>> { if cfg!(debug_assertions) { - info!("fully_perform_op_and_get_region_constraint_data({:?})", self); + info!( + "fully_perform_op_and_get_region_constraint_data({:?})", + self + ); } let mut fulfill_cx = TraitEngine::new(infcx.tcx); @@ -90,223 +96,3 @@ crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { } } } - -crate struct CustomTypeOp { - closure: F, - description: G, -} - -impl CustomTypeOp { - pub(super) fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self - where - F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, - G: Fn() -> String, - { - CustomTypeOp { - closure, - description, - } - } -} - -impl<'gcx, 'tcx, F, R, G> TypeOp<'gcx, 'tcx> for CustomTypeOp -where - F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, - G: Fn() -> String, -{ - type Output = R; - - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - Err(self) - } - - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { - (self.closure)(infcx) - } -} - -impl fmt::Debug for CustomTypeOp -where - G: Fn() -> String, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", (self.description)()) - } -} - -#[derive(Debug)] -crate struct Subtype<'tcx> { - param_env: ParamEnv<'tcx>, - sub: Ty<'tcx>, - sup: Ty<'tcx>, -} - -impl<'tcx> Subtype<'tcx> { - crate fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { - Self { - param_env, - sub, - sup, - } - } -} - -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Subtype<'tcx> { - type Output = (); - - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if self.sub == self.sup { - Ok(()) - } else { - Err(self) - } - } - - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - infcx - .at(&ObligationCause::dummy(), self.param_env) - .sup(self.sup, self.sub) - } -} - -#[derive(Debug)] -crate struct Eq<'tcx> { - param_env: ParamEnv<'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>, -} - -impl<'tcx> Eq<'tcx> { - crate fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { - Self { param_env, a, b } - } -} - -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for Eq<'tcx> { - type Output = (); - - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if self.a == self.b { - Ok(()) - } else { - Err(self) - } - } - - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - infcx - .at(&ObligationCause::dummy(), self.param_env) - .eq(self.a, self.b) - } -} - -#[derive(Debug)] -crate struct ProvePredicates<'tcx> { - obligations: Vec>, -} - -impl<'tcx> ProvePredicates<'tcx> { - crate fn new( - param_env: ParamEnv<'tcx>, - predicates: impl IntoIterator>, - ) -> Self { - ProvePredicates { - obligations: predicates - .into_iter() - .map(|p| Obligation::new(ObligationCause::dummy(), param_env, p)) - .collect(), - } - } -} - -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { - type Output = (); - - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if self.obligations.is_empty() { - Ok(()) - } else { - Err(self) - } - } - - fn perform(self, _infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - Ok(InferOk { - value: (), - obligations: self.obligations, - }) - } -} - -#[derive(Debug)] -crate struct Normalize<'tcx, T> { - param_env: ParamEnv<'tcx>, - value: T, -} - -impl<'tcx, T> Normalize<'tcx, T> -where - T: fmt::Debug + TypeFoldable<'tcx>, -{ - crate fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { - Self { param_env, value } - } -} - -impl<'gcx, 'tcx, T> TypeOp<'gcx, 'tcx> for Normalize<'tcx, T> -where - T: fmt::Debug + TypeFoldable<'tcx>, -{ - type Output = T; - - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if !self.value.has_projections() { - Ok(self.value) - } else { - Err(self) - } - } - - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - let Normalized { value, obligations } = infcx - .at(&ObligationCause::dummy(), self.param_env) - .normalize(&self.value) - .unwrap_or_else(|NoSolution| { - bug!("normalization of `{:?}` failed", self.value,); - }); - Ok(InferOk { value, obligations }) - } -} - -#[derive(Debug)] -crate struct DropckOutlives<'tcx> { - param_env: ParamEnv<'tcx>, - dropped_ty: Ty<'tcx>, -} - -impl<'tcx> DropckOutlives<'tcx> { - crate fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { - DropckOutlives { - param_env, - dropped_ty, - } - } -} - -impl<'gcx, 'tcx> TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { - type Output = Vec>; - - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if trivial_dropck_outlives(tcx, self.dropped_ty) { - Ok(vec![]) - } else { - Err(self) - } - } - - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - Ok(infcx - .at(&ObligationCause::dummy(), self.param_env) - .dropck_outlives(self.dropped_ty)) - } -} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs new file mode 100644 index 0000000000000..35242a5bc292a --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs @@ -0,0 +1,56 @@ +// Copyright 2016 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::{InferCtxt, InferOk, InferResult}; +use rustc::traits::query::NoSolution; +use rustc::traits::{Normalized, ObligationCause}; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{ParamEnv, TyCtxt}; +use std::fmt; + +#[derive(Debug)] +crate struct Normalize<'tcx, T> { + param_env: ParamEnv<'tcx>, + value: T, +} + +impl<'tcx, T> Normalize<'tcx, T> +where + T: fmt::Debug + TypeFoldable<'tcx>, +{ + crate fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { + Self { param_env, value } + } +} + +impl<'gcx, 'tcx, T> super::TypeOp<'gcx, 'tcx> for Normalize<'tcx, T> +where + T: fmt::Debug + TypeFoldable<'tcx>, +{ + type Output = T; + + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + if !self.value.has_projections() { + Ok(self.value) + } else { + Err(self) + } + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + let Normalized { value, obligations } = infcx + .at(&ObligationCause::dummy(), self.param_env) + .normalize(&self.value) + .unwrap_or_else(|NoSolution| { + bug!("normalization of `{:?}` failed", self.value,); + }); + Ok(InferOk { value, obligations }) + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs new file mode 100644 index 0000000000000..655ede4793abe --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs @@ -0,0 +1,48 @@ +// Copyright 2016 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::{InferCtxt, InferResult}; +use rustc::traits::query::dropck_outlives::trivial_dropck_outlives; +use rustc::traits::ObligationCause; +use rustc::ty::subst::Kind; +use rustc::ty::{ParamEnv, Ty, TyCtxt}; + +#[derive(Debug)] +crate struct DropckOutlives<'tcx> { + param_env: ParamEnv<'tcx>, + dropped_ty: Ty<'tcx>, +} + +impl<'tcx> DropckOutlives<'tcx> { + crate fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { + DropckOutlives { + param_env, + dropped_ty, + } + } +} + +impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { + type Output = Vec>; + + fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + if trivial_dropck_outlives(tcx, self.dropped_ty) { + Ok(vec![]) + } else { + Err(self) + } + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + Ok(infcx + .at(&ObligationCause::dummy(), self.param_env) + .dropck_outlives(self.dropped_ty)) + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs new file mode 100644 index 0000000000000..18128d2234e1c --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs @@ -0,0 +1,51 @@ +// Copyright 2016 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::{InferCtxt, InferOk, InferResult}; +use rustc::traits::{Obligation, ObligationCause, PredicateObligation}; +use rustc::ty::{ParamEnv, Predicate, TyCtxt}; + +#[derive(Debug)] +crate struct ProvePredicates<'tcx> { + obligations: Vec>, +} + +impl<'tcx> ProvePredicates<'tcx> { + crate fn new( + param_env: ParamEnv<'tcx>, + predicates: impl IntoIterator>, + ) -> Self { + ProvePredicates { + obligations: predicates + .into_iter() + .map(|p| Obligation::new(ObligationCause::dummy(), param_env, p)) + .collect(), + } + } +} + +impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { + type Output = (); + + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + if self.obligations.is_empty() { + Ok(()) + } else { + Err(self) + } + } + + fn perform(self, _infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + Ok(InferOk { + value: (), + obligations: self.obligations, + }) + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs new file mode 100644 index 0000000000000..83cc3c7d09b3a --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs @@ -0,0 +1,48 @@ +// Copyright 2016 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::{InferCtxt, InferResult}; +use rustc::traits::ObligationCause; +use rustc::ty::{ParamEnv, Ty, TyCtxt}; + +#[derive(Debug)] +crate struct Subtype<'tcx> { + param_env: ParamEnv<'tcx>, + sub: Ty<'tcx>, + sup: Ty<'tcx>, +} + +impl<'tcx> Subtype<'tcx> { + crate fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + Self { + param_env, + sub, + sup, + } + } +} + +impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for Subtype<'tcx> { + type Output = (); + + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + if self.sub == self.sup { + Ok(()) + } else { + Err(self) + } + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + infcx + .at(&ObligationCause::dummy(), self.param_env) + .sup(self.sup, self.sub) + } +} From e72dc794883d81e08d11d88795dbd12479b98137 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 9 Jun 2018 09:43:38 -0400 Subject: [PATCH 31/72] make `convert` so it can apply to a single constraint --- .../nll/type_check/constraint_conversion.rs | 102 +++++++++--------- .../borrow_check/nll/type_check/mod.rs | 2 +- 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 96e99aae69373..900899b9cdebe 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -18,8 +18,8 @@ use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc::infer::region_constraints::{GenericKind, VerifyBound}; use rustc::infer::{self, SubregionOrigin}; use rustc::mir::{Location, Mir}; -use rustc::ty::{self, TyCtxt}; use rustc::ty::subst::UnpackedKind; +use rustc::ty::{self, TyCtxt}; use syntax::codemap::Span; crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { @@ -65,7 +65,13 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } } - pub(super) fn convert(&mut self, query_constraints: &[QueryRegionConstraint<'tcx>]) { + pub(super) fn convert_all(&mut self, query_constraints: &[QueryRegionConstraint<'tcx>]) { + for query_constraint in query_constraints { + self.convert(query_constraint); + } + } + + pub(super) fn convert(&mut self, query_constraint: &QueryRegionConstraint<'tcx>) { debug!("generate: constraints at: {:#?}", self.locations); // Extract out various useful fields we'll need below. @@ -77,57 +83,55 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { .. } = *self; - for query_constraint in query_constraints { - // At the moment, we never generate any "higher-ranked" - // region constraints like `for<'a> 'a: 'b`. At some point - // when we move to universes, we will, and this assertion - // will start to fail. - let ty::OutlivesPredicate(k1, r2) = - query_constraint.no_late_bound_regions().unwrap_or_else(|| { - span_bug!( - self.span(), - "query_constraint {:?} contained bound regions", - query_constraint, - ); - }); - - match k1.unpack() { - UnpackedKind::Lifetime(r1) => { - let r1_vid = self.to_region_vid(r1); - let r2_vid = self.to_region_vid(r2); - self.add_outlives(r1_vid, r2_vid); - - // In the new analysis, all outlives relations etc - // "take effect" at the mid point of the statement - // that requires them, so ignore the `at_location`. - if let Some(all_facts) = &mut self.all_facts { - if let Some(from_location) = self.locations.from_location() { - all_facts.outlives.push(( - r1_vid, - r2_vid, - self.location_table.mid_index(from_location), - )); - } else { - for location in self.location_table.all_points() { - all_facts.outlives.push((r1_vid, r2_vid, location)); - } + // At the moment, we never generate any "higher-ranked" + // region constraints like `for<'a> 'a: 'b`. At some point + // when we move to universes, we will, and this assertion + // will start to fail. + let ty::OutlivesPredicate(k1, r2) = + query_constraint.no_late_bound_regions().unwrap_or_else(|| { + span_bug!( + self.span(), + "query_constraint {:?} contained bound regions", + query_constraint, + ); + }); + + match k1.unpack() { + UnpackedKind::Lifetime(r1) => { + let r1_vid = self.to_region_vid(r1); + let r2_vid = self.to_region_vid(r2); + self.add_outlives(r1_vid, r2_vid); + + // In the new analysis, all outlives relations etc + // "take effect" at the mid point of the statement + // that requires them, so ignore the `at_location`. + if let Some(all_facts) = &mut self.all_facts { + if let Some(from_location) = self.locations.from_location() { + all_facts.outlives.push(( + r1_vid, + r2_vid, + self.location_table.mid_index(from_location), + )); + } else { + for location in self.location_table.all_points() { + all_facts.outlives.push((r1_vid, r2_vid, location)); } } } + } - UnpackedKind::Type(t1) => { - // we don't actually use this for anything, but - // the `TypeOutlives` code needs an origin. - let origin = infer::RelateParamBound(self.span(), t1); - - TypeOutlives::new( - &mut *self, - tcx, - region_bound_pairs, - implicit_region_bound, - param_env, - ).type_must_outlive(origin, t1, r2); - } + UnpackedKind::Type(t1) => { + // we don't actually use this for anything, but + // the `TypeOutlives` code needs an origin. + let origin = infer::RelateParamBound(self.span(), t1); + + TypeOutlives::new( + &mut *self, + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ).type_must_outlive(origin, t1, r2); } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d7812d86574fd..4a507300f7c9e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -765,7 +765,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { &mut self.constraints.outlives_constraints, &mut self.constraints.type_tests, &mut borrowck_context.all_facts, - ).convert(&data); + ).convert_all(&data); } } From 75e3a9c8fd4283ab797b5bef7e2a36eda7853cec Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 9 Jun 2018 09:50:51 -0400 Subject: [PATCH 32/72] rename `instantiate_query_result` --- src/librustc/infer/canonical/query_result.rs | 2 +- src/librustc/traits/query/dropck_outlives.rs | 2 +- src/librustc/traits/query/normalize.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index d61434daf9934..b6d8f6ae946e8 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -145,7 +145,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// out the [chapter in the rustc guide][c]. /// /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#processing-the-canonicalized-query-result - pub fn instantiate_query_result( + pub fn instantiate_query_result_and_region_obligations( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 671809c059ad8..3bce25b3eff4f 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -53,7 +53,7 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { debug!("c_ty = {:?}", c_ty); match &gcx.dropck_outlives(c_ty) { Ok(result) if result.is_proven() => { - match self.infcx.instantiate_query_result( + match self.infcx.instantiate_query_result_and_region_obligations( self.cause, self.param_env, &orig_values, diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index 22b47458bcecc..f7ee67e810b55 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -162,7 +162,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx return ty; } - match self.infcx.instantiate_query_result( + match self.infcx.instantiate_query_result_and_region_obligations( self.cause, self.param_env, &orig_values, From d748dc5db04f8287510984caa3ecb6465836ecc2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 9 Jun 2018 10:07:03 -0400 Subject: [PATCH 33/72] extract `query_result_substitution` helper --- src/librustc/infer/canonical/query_result.rs | 68 +++++++++++++++----- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index b6d8f6ae946e8..d40daf73115fc 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -152,6 +152,49 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { original_values: &CanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, ) -> InferResult<'tcx, R> + where + R: Debug + TypeFoldable<'tcx>, + { + let InferOk { value: result_subst, mut obligations } = self.query_result_substitution( + cause, + param_env, + original_values, + query_result, + )?; + + obligations.extend(self.query_region_constraints_into_obligations( + cause, + param_env, + &query_result.value.region_constraints, + &result_subst, + )); + + let user_result: R = + query_result.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value); + + Ok(InferOk { + value: user_result, + obligations, + }) + } + + /// Given the original values and the (canonicalized) result from + /// computing a query, returns a substitution that can be applied + /// to the query result to convert the result back into the + /// original namespace. + /// + /// The substitution also comes accompanied with subobligations + /// that arose from unification; these might occur if (for + /// example) we are doing lazy normalization and the value + /// assigned to a type variable is unified with an unnormalized + /// projection. + pub fn query_result_substitution( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &CanonicalVarValues<'tcx>, + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, + ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> where R: Debug + TypeFoldable<'tcx>, { @@ -201,7 +244,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // Create a result substitution: if we found a value for a // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. - let result_subst = &CanonicalVarValues { + let result_subst = CanonicalVarValues { var_values: query_result .variables .iter() @@ -217,25 +260,16 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // the input with the value found in the query // post-substitution. Often, but not always, this is a no-op, // because we already found the mapping in the first step. - let substituted_values = |index: CanonicalVar| -> Kind<'tcx> { - query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index]) + let obligations = { + let substituted_values = |index: CanonicalVar| -> Kind<'tcx> { + query_result.substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]) + }; + self.unify_canonical_vars(cause, param_env, original_values, substituted_values)? + .into_obligations() }; - let mut obligations = self - .unify_canonical_vars(cause, param_env, original_values, substituted_values)? - .into_obligations(); - - obligations.extend(self.query_region_constraints_into_obligations( - cause, - param_env, - &query_result.value.region_constraints, - result_subst, - )); - - let user_result: R = - query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value); Ok(InferOk { - value: user_result, + value: result_subst, obligations, }) } From 1d664622b6e46df7e41c0132ebbe60abcf482a21 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 08:41:21 -0400 Subject: [PATCH 34/72] remove `Canonicalization` trait, which serves no purpose --- src/librustc/infer/canonical/canonicalizer.rs | 41 +++++++++---------- src/librustc/infer/canonical/mod.rs | 31 +------------- src/librustc/infer/canonical/query_result.rs | 8 ++-- src/librustc/traits/mod.rs | 30 -------------- src/librustc/traits/query/dropck_outlives.rs | 20 +++------ .../traits/query/evaluate_obligation.rs | 14 ------- src/librustc/traits/query/normalize.rs | 13 ------ src/librustc/ty/mod.rs | 11 +---- src/librustc_traits/chalk_context.rs | 15 +------ 9 files changed, 32 insertions(+), 151 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 677063fcf881e..aaa5a01e5016a 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -17,13 +17,13 @@ use infer::canonical::{ Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, CanonicalVarValues, - Canonicalize, + Canonicalized, }; use infer::InferCtxt; use std::sync::atomic::Ordering; use ty::fold::{TypeFoldable, TypeFolder}; use ty::subst::Kind; -use ty::{self, CanonicalVar, Slice, Ty, TyCtxt, TypeFlags}; +use ty::{self, CanonicalVar, Lift, Slice, Ty, TyCtxt, TypeFlags}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::IndexVec; @@ -44,9 +44,12 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// out the [chapter in the rustc guide][c]. /// /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query - pub fn canonicalize_query(&self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + pub fn canonicalize_query( + &self, + value: &V, + ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) where - V: Canonicalize<'gcx, 'tcx>, + V: TypeFoldable<'tcx> + Lift<'gcx>, { self.tcx .sess @@ -90,9 +93,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { pub fn canonicalize_response( &self, value: &V, - ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) where - V: Canonicalize<'gcx, 'tcx>, + V: TypeFoldable<'tcx> + Lift<'gcx>, { Canonicalizer::canonicalize( value, @@ -233,9 +236,9 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, tcx: TyCtxt<'cx, 'gcx, 'tcx>, canonicalize_all_free_regions: CanonicalizeAllFreeRegions, - ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) where - V: Canonicalize<'gcx, 'tcx>, + V: TypeFoldable<'tcx> + Lift<'gcx>, { debug_assert!( !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS), @@ -254,13 +257,10 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { // Fast path: nothing that needs to be canonicalized. if !value.has_type_flags(needs_canonical_flags) { let out_value = gcx.lift(value).unwrap(); - let canon_value = V::intern( - gcx, - Canonical { - variables: Slice::empty(), - value: out_value, - }, - ); + let canon_value = Canonical { + variables: Slice::empty(), + value: out_value, + }; let values = CanonicalVarValues { var_values: IndexVec::default(), }; @@ -291,13 +291,10 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables.raw); - let canonical_value = V::intern( - gcx, - Canonical { - variables: canonical_variables, - value: out_value, - }, - ); + let canonical_value = Canonical { + variables: canonical_variables, + value: out_value, + }; let canonical_var_values = CanonicalVarValues { var_values: canonicalizer.var_values, }; diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 6af8fee663159..62424ff9226df 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -35,7 +35,6 @@ use infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use serialize::UseSpecializedDecodable; -use std::fmt::Debug; use std::ops::Index; use syntax::codemap::Span; use ty::fold::TypeFoldable; @@ -124,6 +123,8 @@ pub struct QueryResult<'tcx, R> { pub value: R, } +pub type Canonicalized<'gcx, V> = Canonical<'gcx, >::Lifted>; + pub type CanonicalizedQueryResult<'gcx, T> = Lrc>::Lifted>>>; @@ -184,19 +185,6 @@ impl<'tcx, R> Canonical<'tcx, QueryResult<'tcx, R>> { pub type QueryRegionConstraint<'tcx> = ty::Binder, Region<'tcx>>>; -/// Trait implemented by values that can be canonicalized. It mainly -/// serves to identify the interning table we will use. -pub trait Canonicalize<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { - type Canonicalized: 'gcx + Debug; - - /// After a value has been fully canonicalized and lifted, this - /// method will allocate it in a global arena. - fn intern( - gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized; -} - impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Creates a substitution S for the canonical value with fresh /// inference variables and applies it to the canonical value. @@ -344,18 +332,3 @@ impl<'tcx> Index for CanonicalVarValues<'tcx> { &self.var_values[value] } } - -impl<'gcx: 'tcx, 'tcx, T> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, T> -where - T: TypeFoldable<'tcx> + Lift<'gcx>, -{ - // we ought to intern this, but I'm too lazy just now - type Canonicalized = Lrc>>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - Lrc::new(value) - } -} diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index d40daf73115fc..3fc68994e2197 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -19,13 +19,14 @@ use infer::canonical::substitute::substitute_value; use infer::canonical::{ - Canonical, CanonicalVarValues, Canonicalize, CanonicalizedQueryResult, Certainty, + Canonical, CanonicalVarValues, CanonicalizedQueryResult, Certainty, QueryRegionConstraint, QueryResult, }; use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::sync::Lrc; use std::fmt::Debug; use syntax::ast; use traits::query::NoSolution; @@ -72,7 +73,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { canonical_result ); - Ok(canonical_result) + Ok(Lrc::new(canonical_result)) } /// Helper for `make_canonicalized_query_result` that does @@ -84,8 +85,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { fulfill_cx: &mut FulfillmentContext<'tcx>, ) -> Result, NoSolution> where - T: Debug, - QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, + T: Debug + TypeFoldable<'tcx> + Lift<'gcx>, { let tcx = self.tcx; diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 15f0b8eebc1db..e9e2695fa5c7f 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -27,7 +27,6 @@ use ty::subst::Substs; use ty::{self, AdtKind, Slice, Ty, TyCtxt, GenericParamDefKind, ToPredicate}; use ty::error::{ExpectedFound, TypeError}; use ty::fold::{TypeFolder, TypeFoldable, TypeVisitor}; -use infer::canonical::{Canonical, Canonicalize}; use infer::{InferCtxt}; use rustc_data_structures::sync::Lrc; @@ -1015,18 +1014,6 @@ pub fn provide(providers: &mut ty::query::Providers) { }; } -impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, Goal<'tcx>> { - // we ought to intern this, but I'm too lazy just now - type Canonicalized = Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - value - } -} - pub trait ExClauseFold<'tcx> where Self: chalk_engine::context::Context + Clone, @@ -1053,20 +1040,3 @@ where tcx: TyCtxt<'a, 'gcx, 'tcx>, ) -> Option; } - -impl<'gcx: 'tcx, 'tcx, C> Canonicalize<'gcx, 'tcx> for chalk_engine::ExClause -where - C: chalk_engine::context::Context + Clone, - C: ExClauseLift<'gcx> + ExClauseFold<'tcx>, - C::Substitution: Clone, - C::RegionConstraint: Clone, -{ - type Canonicalized = Canonical<'gcx, C::LiftedExClause>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - value - } -} diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 3bce25b3eff4f..b843ae8f11afd 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -9,12 +9,10 @@ // except according to those terms. use infer::at::At; -use infer::canonical::{Canonical, Canonicalize}; use infer::InferOk; use std::iter::FromIterator; -use traits::query::CanonicalTyGoal; -use ty::{self, Ty, TyCtxt}; use ty::subst::Kind; +use ty::{self, Ty, TyCtxt}; impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { /// Given a type `ty` of some value being dropped, computes a set @@ -44,7 +42,10 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { // any destructor. let tcx = self.infcx.tcx; if trivial_dropck_outlives(tcx, ty) { - return InferOk { value: vec![], obligations: vec![] }; + return InferOk { + value: vec![], + obligations: vec![], + }; } let gcx = tcx.global_tcx(); @@ -152,17 +153,6 @@ impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { 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 diff --git a/src/librustc/traits/query/evaluate_obligation.rs b/src/librustc/traits/query/evaluate_obligation.rs index 4e028cac49abe..c81d1123d42af 100644 --- a/src/librustc/traits/query/evaluate_obligation.rs +++ b/src/librustc/traits/query/evaluate_obligation.rs @@ -9,11 +9,8 @@ // except according to those terms. use infer::InferCtxt; -use infer::canonical::{Canonical, Canonicalize}; use traits::{EvaluationResult, PredicateObligation, SelectionContext, TraitQueryMode, OverflowError}; -use traits::query::CanonicalPredicateGoal; -use ty::{ParamEnvAnd, Predicate, TyCtxt}; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Evaluates whether the predicate can be satisfied (by any means) @@ -57,14 +54,3 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } } } - -impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ParamEnvAnd<'tcx, Predicate<'tcx>> { - type Canonicalized = CanonicalPredicateGoal<'gcx>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - value - } -} diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index f7ee67e810b55..d459c2d82ad73 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -14,11 +14,9 @@ use infer::{InferCtxt, InferOk}; use infer::at::At; -use infer::canonical::{Canonical, Canonicalize}; use middle::const_val::ConstVal; use mir::interpret::GlobalId; use traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; -use traits::query::CanonicalProjectionGoal; use traits::project::Normalized; use ty::{self, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder}; @@ -250,17 +248,6 @@ BraceStructLiftImpl! { } } -impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>> { - type Canonicalized = CanonicalProjectionGoal<'gcx>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>, - ) -> Self::Canonicalized { - value - } -} - impl_stable_hash_for!(struct NormalizationResult<'tcx> { normalized_ty }); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ce70983145579..e89a022f81870 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -21,7 +21,7 @@ use hir::map::DefPathData; use hir::svh::Svh; use ich::Fingerprint; use ich::StableHashingContext; -use infer::canonical::{Canonical, Canonicalize}; +use infer::canonical::Canonical; use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; use middle::privacy::AccessLevels; use middle::resolve_lifetime::ObjectLifetimeDefault; @@ -591,15 +591,6 @@ impl<'tcx> serialize::UseSpecializedDecodable for Ty<'tcx> {} pub type CanonicalTy<'gcx> = Canonical<'gcx, Ty<'gcx>>; -impl <'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for Ty<'tcx> { - type Canonicalized = CanonicalTy<'gcx>; - - fn intern(_gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, Self::Lifted>) -> Self::Canonicalized { - value - } -} - extern { /// A dummy type used to force Slice to by unsized without requiring fat pointers type OpaqueSliceContents; diff --git a/src/librustc_traits/chalk_context.rs b/src/librustc_traits/chalk_context.rs index a1242621cb18c..6062fe03e6a16 100644 --- a/src/librustc_traits/chalk_context.rs +++ b/src/librustc_traits/chalk_context.rs @@ -10,9 +10,7 @@ use chalk_engine::fallible::Fallible as ChalkEngineFallible; use chalk_engine::{context, hh::HhGoal, DelayedLiteral, ExClause}; -use rustc::infer::canonical::{ - Canonical, CanonicalVarValues, Canonicalize, QueryRegionConstraint, QueryResult, -}; +use rustc::infer::canonical::{Canonical, CanonicalVarValues, QueryRegionConstraint, QueryResult}; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use rustc::traits::{ WellFormed, @@ -519,14 +517,3 @@ BraceStructLiftImpl! { subst, constraints } } - -impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ConstrainedSubst<'tcx> { - type Canonicalized = Canonical<'gcx, ConstrainedSubst<'gcx>>; - - fn intern( - _gcx: TyCtxt<'_, 'gcx, 'gcx>, - value: Canonical<'gcx, ConstrainedSubst<'gcx>>, - ) -> Self::Canonicalized { - value - } -} From 3b446b4b914945209d917a02572909717c6bd0b9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 04:11:37 -0400 Subject: [PATCH 35/72] introduce `QueryTypeOp` trait and use it for `eq` --- .../borrow_check/nll/type_check/type_op/eq.rs | 59 ++++++++++++++---- .../nll/type_check/type_op/mod.rs | 62 ++++++++++++++++++- 2 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs index b062eff0733c2..ca10f0e8cea93 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs @@ -8,11 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::{InferCtxt, InferResult}; -use rustc::traits::ObligationCause; -use rustc::ty::{ParamEnv, Ty, TyCtxt}; +use rustc::infer::canonical::{CanonicalizedQueryResult, Canonical}; +use rustc::traits::query::NoSolution; +use rustc::traits::{FulfillmentContext, ObligationCause}; +use rustc::ty::{self, ParamEnv, Ty, TyCtxt}; +use syntax::codemap::DUMMY_SP; -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] crate struct Eq<'tcx> { param_env: ParamEnv<'tcx>, a: Ty<'tcx>, @@ -25,10 +27,10 @@ impl<'tcx> Eq<'tcx> { } } -impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for Eq<'tcx> { - type Output = (); +impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { + type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if self.a == self.b { Ok(()) } else { @@ -36,9 +38,44 @@ impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for Eq<'tcx> { } } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - infcx - .at(&ObligationCause::dummy(), self.param_env) - .eq(self.a, self.b) + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonical<'gcx, Eq<'gcx>>, + ) -> CanonicalizedQueryResult<'gcx, ()> { + let tcx = tcx.global_tcx(); + tcx.infer_ctxt() + .enter(|ref infcx| { + let (Eq { param_env, a, b }, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); + let fulfill_cx = &mut FulfillmentContext::new(); + let obligations = match infcx.at(&ObligationCause::dummy(), param_env).eq(a, b) { + Ok(v) => v.into_obligations(), + Err(_) => return Err(NoSolution), + }; + fulfill_cx.register_predicate_obligations(infcx, obligations); + infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) + }) + .unwrap() + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for Eq<'tcx> { + param_env, + a, + b, + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for Eq<'a> { + type Lifted = Eq<'tcx>; + param_env, + a, + b, } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs index 448dfd853c1b3..4d9dd915f0373 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs @@ -9,11 +9,12 @@ // except according to those terms. use rustc::infer::canonical::query_result; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::{Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint}; use rustc::infer::{InferCtxt, InferOk, InferResult}; use rustc::traits::{ObligationCause, TraitEngine}; use rustc::ty::error::TypeError; -use rustc::ty::TyCtxt; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{Lift, ParamEnv, TyCtxt}; use std::fmt; use std::rc::Rc; use syntax::codemap::DUMMY_SP; @@ -21,8 +22,8 @@ use syntax::codemap::DUMMY_SP; crate mod custom; crate mod eq; crate mod normalize; -crate mod predicates; crate mod outlives; +crate mod predicates; crate mod subtype; crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { @@ -96,3 +97,58 @@ crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { } } } + +type Lifted<'gcx, T> = >::Lifted; + +crate trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { + type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; + + /// Micro-optimization: returns `Ok(x)` if we can trivially + /// produce the output, else returns `Err(self)` back. + fn trivial_noop( + self, + tcx: TyCtxt<'_, 'gcx, 'tcx>, + ) -> Result, Self>; + + fn param_env(&self) -> ParamEnv<'tcx>; + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Self>, + ) -> CanonicalizedQueryResult<'gcx, Self::QueryResult>; +} + +impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for Q +where + Q: QueryTypeOp<'gcx, 'tcx>, + Lifted<'gcx, Q::QueryResult>: TypeFoldable<'tcx>, +{ + type Output = Lifted<'gcx, Q::QueryResult>; + + fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + QueryTypeOp::trivial_noop(self, tcx) + } + + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + let param_env = self.param_env(); + + let (canonical_self, canonical_var_values) = infcx.canonicalize_query(&self); + let canonical_result = Q::perform_query(infcx.tcx, canonical_self); + + // FIXME: This is not the most efficient setup. The + // `instantiate_query_result_and_region_obligations` basically + // takes the `QueryRegionConstraint` values that we ultimately + // want to use and converts them into obligations. We return + // those to our caller, which will convert them into AST + // region constraints; we then convert *those* back into + // `QueryRegionConstraint` and ultimately into NLL + // constraints. We should cut out the middleman but that will + // take a bit of refactoring. + infcx.instantiate_query_result_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + &canonical_result, + ) + } +} From 265552258048b2008975d98de75c7e536bc4f894 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 10:03:14 -0400 Subject: [PATCH 36/72] move `type_op` into `rustc` --- src/librustc/traits/query/mod.rs | 1 + .../traits/query}/type_op/custom.rs | 10 +++---- .../traits/query}/type_op/eq.rs | 12 ++++---- .../traits/query}/type_op/mod.rs | 30 +++++++++---------- .../traits/query}/type_op/normalize.rs | 14 ++++----- .../traits/query}/type_op/outlives.rs | 14 ++++----- .../traits/query}/type_op/predicates.rs | 10 +++---- .../traits/query}/type_op/subtype.rs | 10 +++---- .../nll/type_check/input_output.rs | 2 +- .../borrow_check/nll/type_check/liveness.rs | 4 +-- .../borrow_check/nll/type_check/mod.rs | 19 +++++------- 11 files changed, 61 insertions(+), 65 deletions(-) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/custom.rs (84%) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/eq.rs (87%) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/mod.rs (90%) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/normalize.rs (82%) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/outlives.rs (78%) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/predicates.rs (86%) rename src/{librustc_mir/borrow_check/nll/type_check => librustc/traits/query}/type_op/subtype.rs (83%) diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 096633ddab2f7..7181a94acff31 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -22,6 +22,7 @@ pub mod dropck_outlives; pub mod evaluate_obligation; pub mod normalize; pub mod normalize_erasing_regions; +pub mod type_op; pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs similarity index 84% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs rename to src/librustc/traits/query/type_op/custom.rs index ce17cab8dcb09..65b2ab31eba4c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -8,17 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::{InferCtxt, InferResult}; -use rustc::ty::TyCtxt; +use infer::{InferCtxt, InferResult}; +use ty::TyCtxt; use std::fmt; -crate struct CustomTypeOp { +pub struct CustomTypeOp { closure: F, description: G, } impl CustomTypeOp { - crate fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self + pub fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self where F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, G: Fn() -> String, @@ -32,7 +32,7 @@ impl CustomTypeOp { impl<'gcx, 'tcx, F, R, G> super::TypeOp<'gcx, 'tcx> for CustomTypeOp where - F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'gcx, 'tcx>) -> InferResult<'tcx, R>, G: Fn() -> String, { type Output = R; diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs similarity index 87% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs rename to src/librustc/traits/query/type_op/eq.rs index ca10f0e8cea93..9dad9fa4e0a17 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -8,21 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::canonical::{CanonicalizedQueryResult, Canonical}; -use rustc::traits::query::NoSolution; -use rustc::traits::{FulfillmentContext, ObligationCause}; -use rustc::ty::{self, ParamEnv, Ty, TyCtxt}; +use infer::canonical::{CanonicalizedQueryResult, Canonical}; +use traits::query::NoSolution; +use traits::{FulfillmentContext, ObligationCause}; +use ty::{self, ParamEnv, Ty, TyCtxt}; use syntax::codemap::DUMMY_SP; #[derive(Copy, Clone, Debug)] -crate struct Eq<'tcx> { +pub struct Eq<'tcx> { param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, } impl<'tcx> Eq<'tcx> { - crate fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + pub fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { Self { param_env, a, b } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs similarity index 90% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs rename to src/librustc/traits/query/type_op/mod.rs index 4d9dd915f0373..0028aaa40446e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -8,25 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::canonical::query_result; -use rustc::infer::canonical::{Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint}; -use rustc::infer::{InferCtxt, InferOk, InferResult}; -use rustc::traits::{ObligationCause, TraitEngine}; -use rustc::ty::error::TypeError; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{Lift, ParamEnv, TyCtxt}; +use infer::canonical::query_result; +use infer::canonical::{Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint}; +use infer::{InferCtxt, InferOk, InferResult}; +use traits::{ObligationCause, TraitEngine}; +use ty::error::TypeError; +use ty::fold::TypeFoldable; +use ty::{Lift, ParamEnv, TyCtxt}; use std::fmt; use std::rc::Rc; use syntax::codemap::DUMMY_SP; -crate mod custom; -crate mod eq; -crate mod normalize; -crate mod outlives; -crate mod predicates; -crate mod subtype; +pub mod custom; +pub mod eq; +pub mod normalize; +pub mod outlives; +pub mod predicates; +pub mod subtype; -crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { +pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; /// Micro-optimization: returns `Ok(x)` if we can trivially @@ -100,7 +100,7 @@ crate trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Lifted<'gcx, T> = >::Lifted; -crate trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { +pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; /// Micro-optimization: returns `Ok(x)` if we can trivially diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs similarity index 82% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs rename to src/librustc/traits/query/type_op/normalize.rs index 35242a5bc292a..a363f6f213cb1 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -8,15 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::{InferCtxt, InferOk, InferResult}; -use rustc::traits::query::NoSolution; -use rustc::traits::{Normalized, ObligationCause}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{ParamEnv, TyCtxt}; +use infer::{InferCtxt, InferOk, InferResult}; +use traits::query::NoSolution; +use traits::{Normalized, ObligationCause}; +use ty::fold::TypeFoldable; +use ty::{ParamEnv, TyCtxt}; use std::fmt; #[derive(Debug)] -crate struct Normalize<'tcx, T> { +pub struct Normalize<'tcx, T> { param_env: ParamEnv<'tcx>, value: T, } @@ -25,7 +25,7 @@ impl<'tcx, T> Normalize<'tcx, T> where T: fmt::Debug + TypeFoldable<'tcx>, { - crate fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { + pub fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { Self { param_env, value } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs similarity index 78% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs rename to src/librustc/traits/query/type_op/outlives.rs index 655ede4793abe..f6c60bcaf38cd 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -8,20 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::{InferCtxt, InferResult}; -use rustc::traits::query::dropck_outlives::trivial_dropck_outlives; -use rustc::traits::ObligationCause; -use rustc::ty::subst::Kind; -use rustc::ty::{ParamEnv, Ty, TyCtxt}; +use infer::{InferCtxt, InferResult}; +use traits::query::dropck_outlives::trivial_dropck_outlives; +use traits::ObligationCause; +use ty::subst::Kind; +use ty::{ParamEnv, Ty, TyCtxt}; #[derive(Debug)] -crate struct DropckOutlives<'tcx> { +pub struct DropckOutlives<'tcx> { param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>, } impl<'tcx> DropckOutlives<'tcx> { - crate fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { + pub fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { DropckOutlives { param_env, dropped_ty, diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs b/src/librustc/traits/query/type_op/predicates.rs similarity index 86% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs rename to src/librustc/traits/query/type_op/predicates.rs index 18128d2234e1c..d729ce1e0ebd7 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/predicates.rs +++ b/src/librustc/traits/query/type_op/predicates.rs @@ -8,17 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::{InferCtxt, InferOk, InferResult}; -use rustc::traits::{Obligation, ObligationCause, PredicateObligation}; -use rustc::ty::{ParamEnv, Predicate, TyCtxt}; +use infer::{InferCtxt, InferOk, InferResult}; +use traits::{Obligation, ObligationCause, PredicateObligation}; +use ty::{ParamEnv, Predicate, TyCtxt}; #[derive(Debug)] -crate struct ProvePredicates<'tcx> { +pub struct ProvePredicates<'tcx> { obligations: Vec>, } impl<'tcx> ProvePredicates<'tcx> { - crate fn new( + pub fn new( param_env: ParamEnv<'tcx>, predicates: impl IntoIterator>, ) -> Self { diff --git a/src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs similarity index 83% rename from src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs rename to src/librustc/traits/query/type_op/subtype.rs index 83cc3c7d09b3a..e9adabc8b0871 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -8,19 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::infer::{InferCtxt, InferResult}; -use rustc::traits::ObligationCause; -use rustc::ty::{ParamEnv, Ty, TyCtxt}; +use infer::{InferCtxt, InferResult}; +use traits::ObligationCause; +use ty::{ParamEnv, Ty, TyCtxt}; #[derive(Debug)] -crate struct Subtype<'tcx> { +pub struct Subtype<'tcx> { param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>, } impl<'tcx> Subtype<'tcx> { - crate fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + pub fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { Self { param_env, sub, diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index a127128818e15..770a0614811dc 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -18,12 +18,12 @@ //! contain revealed `impl Trait` values). use borrow_check::nll::renumber; -use borrow_check::nll::type_check::type_op::custom::CustomTypeOp; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferOk; use rustc::mir::visit::TyContext; use rustc::mir::*; +use rustc::traits::query::type_op::custom::CustomTypeOp; use rustc::traits::{ObligationCause, PredicateObligations}; use rustc::ty::subst::Subst; use rustc::ty::Ty; 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 780269930913d..b6cdbfa618187 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -9,8 +9,6 @@ // except according to those terms. use borrow_check::nll::region_infer::Cause; -use borrow_check::nll::type_check::type_op::TypeOp; -use borrow_check::nll::type_check::type_op::outlives::DropckOutlives; use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; @@ -18,6 +16,8 @@ use dataflow::{FlowAtLocation, FlowsAtLocation}; use rustc::infer::canonical::QueryRegionConstraint; use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::traits::query::type_op::outlives::DropckOutlives; +use rustc::traits::query::type_op::TypeOp; use rustc::ty::subst::Kind; use rustc::ty::{Ty, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 4a507300f7c9e..865ee66cccfde 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -27,6 +27,7 @@ use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; +use rustc::traits::query::type_op; use rustc::traits::ObligationCause; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; @@ -66,7 +67,6 @@ macro_rules! span_mirbug_and_err { mod constraint_conversion; mod input_output; mod liveness; -mod type_op; /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to @@ -776,7 +776,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { locations: Locations, ) -> UnitResult<'tcx> { let param_env = self.param_env; - self.fully_perform_op(locations, type_op::subtype::Subtype::new(param_env, sub, sup)) + self.fully_perform_op( + locations, + type_op::subtype::Subtype::new(param_env, sub, sup), + ) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { @@ -1623,16 +1626,8 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _ = type_check_internal( - &infcx, - def_id, - param_env, - mir, - &[], - None, - None, - &mut |_| (), - ); + let _ = + type_check_internal(&infcx, def_id, param_env, mir, &[], None, None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) From 1acffada44df88112d23941cd96ea120ea4b8daf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 12 Jun 2018 14:33:50 -0400 Subject: [PATCH 37/72] introduce `canonicalize_hr_query_hack` As the comment explains, this is needed to prevent subtype from going awry in higher-ranked cases, due to #33684. The proper fix here is introducing universes (#48536). --- src/librustc/infer/canonical/canonicalizer.rs | 79 ++++++++++++++++--- src/librustc/traits/query/type_op/mod.rs | 6 +- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index aaa5a01e5016a..8b67f04e0201c 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -61,7 +61,10 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { value, Some(self), self.tcx, - CanonicalizeAllFreeRegions(true), + CanonicalizeRegionMode { + static_region: true, + other_free_regions: true, + }, ) } @@ -101,7 +104,43 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { value, Some(self), self.tcx, - CanonicalizeAllFreeRegions(false), + CanonicalizeRegionMode { + static_region: false, + other_free_regions: false, + }, + ) + } + + /// A hacky variant of `canonicalize_query` that does not + /// canonicalize `'static`. Unfortunately, the existing leak + /// check treaks `'static` differently in some cases (see also + /// #33684), so if we are performing an operation that may need to + /// prove "leak-check" related things, we leave `'static` + /// alone. + /// + /// FIXME(#48536) -- once we have universes, we can remove this and just use + /// `canonicalize_query`. + pub fn canonicalize_hr_query_hack( + &self, + value: &V, + ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) + where + V: TypeFoldable<'tcx> + Lift<'gcx>, + { + self.tcx + .sess + .perf_stats + .queries_canonicalized + .fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + CanonicalizeRegionMode { + static_region: false, + other_free_regions: true, + }, ) } } @@ -110,7 +149,16 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// a canonical var. This is used to make queries as generic as /// possible. For example, the query `F: Foo<'static>` would be /// canonicalized to `F: Foo<'0>`. -struct CanonicalizeAllFreeRegions(pub bool); +struct CanonicalizeRegionMode { + static_region: bool, + other_free_regions: bool, +} + +impl CanonicalizeRegionMode { + fn any(&self) -> bool { + self.static_region || self.other_free_regions + } +} struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, @@ -118,7 +166,7 @@ struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { variables: IndexVec, indices: FxHashMap, CanonicalVar>, var_values: IndexVec>, - canonicalize_all_free_regions: CanonicalizeAllFreeRegions, + canonicalize_region_mode: CanonicalizeRegionMode, needs_canonical_flags: TypeFlags, } @@ -152,14 +200,25 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> self.tcx().mk_region(ty::ReCanonical(cvar)) } - ty::ReStatic - | ty::ReEarlyBound(..) + ty::ReStatic => { + if self.canonicalize_region_mode.static_region { + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Region, + }; + let cvar = self.canonical_var(info, r.into()); + self.tcx().mk_region(ty::ReCanonical(cvar)) + } else { + r + } + } + + ty::ReEarlyBound(..) | ty::ReFree(_) | ty::ReScope(_) | ty::ReSkolemized(..) | ty::ReEmpty | ty::ReErased => { - if self.canonicalize_all_free_regions.0 { + if self.canonicalize_region_mode.other_free_regions { let info = CanonicalVarInfo { kind: CanonicalVarKind::Region, }; @@ -235,7 +294,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { value: &V, infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, tcx: TyCtxt<'cx, 'gcx, 'tcx>, - canonicalize_all_free_regions: CanonicalizeAllFreeRegions, + canonicalize_region_mode: CanonicalizeRegionMode, ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) where V: TypeFoldable<'tcx> + Lift<'gcx>, @@ -246,7 +305,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { value, ); - let needs_canonical_flags = if canonicalize_all_free_regions.0 { + let needs_canonical_flags = if canonicalize_region_mode.any() { TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX } else { TypeFlags::KEEP_IN_LOCAL_TCX @@ -270,7 +329,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { let mut canonicalizer = Canonicalizer { infcx, tcx, - canonicalize_all_free_regions, + canonicalize_region_mode, needs_canonical_flags, variables: IndexVec::default(), indices: FxHashMap::default(), diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 0028aaa40446e..6436e75158ea3 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -132,7 +132,11 @@ where fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { let param_env = self.param_env(); - let (canonical_self, canonical_var_values) = infcx.canonicalize_query(&self); + // FIXME(#33684) -- We need to use + // `canonicalize_hr_query_hack` here because of things like + // the subtype query, which go awry around `'static` + // otherwise. + let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&self); let canonical_result = Q::perform_query(infcx.tcx, canonical_self); // FIXME: This is not the most efficient setup. The From 71ce2e7eb6d941def725e85b8e9a23c75472ec62 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 10:33:37 -0400 Subject: [PATCH 38/72] make `Eq` a true query --- src/librustc/dep_graph/dep_node.rs | 6 +++-- src/librustc/traits/query/mod.rs | 3 +++ src/librustc/traits/query/type_op/eq.rs | 32 +++++++--------------- src/librustc/ty/query/config.rs | 10 ++++++- src/librustc/ty/query/keys.rs | 29 +++++--------------- src/librustc/ty/query/mod.rs | 10 ++++++- src/librustc/ty/query/plumbing.rs | 1 + src/librustc_traits/lib.rs | 2 ++ src/librustc_traits/type_op_eq.rs | 35 +++++++++++++++++++++++++ 9 files changed, 80 insertions(+), 48 deletions(-) create mode 100644 src/librustc_traits/type_op_eq.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 3a152ccd0c971..efaebfd06108f 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -70,8 +70,9 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; -use traits::query::{CanonicalProjectionGoal, - CanonicalTyGoal, CanonicalPredicateGoal}; +use traits::query::{ + CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalPredicateGoal, +}; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; @@ -647,6 +648,7 @@ define_dep_nodes!( <'tcx> [] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>), [] DropckOutlives(CanonicalTyGoal<'tcx>), [] EvaluateObligation(CanonicalPredicateGoal<'tcx>), + [] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 7181a94acff31..3ee0c0cdd0b6e 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -32,6 +32,9 @@ pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>> pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; +pub type CanonicalTypeOpEqGoal<'tcx> = + Canonical<'tcx, type_op::eq::Eq<'tcx>>; + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 9dad9fa4e0a17..8925fa12f03a7 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -8,17 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{CanonicalizedQueryResult, Canonical}; -use traits::query::NoSolution; -use traits::{FulfillmentContext, ObligationCause}; +use infer::canonical::{Canonical, CanonicalizedQueryResult}; use ty::{self, ParamEnv, Ty, TyCtxt}; -use syntax::codemap::DUMMY_SP; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Eq<'tcx> { - param_env: ParamEnv<'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>, + pub param_env: ParamEnv<'tcx>, + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, } impl<'tcx> Eq<'tcx> { @@ -46,20 +43,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonical<'gcx, Eq<'gcx>>, ) -> CanonicalizedQueryResult<'gcx, ()> { - let tcx = tcx.global_tcx(); - tcx.infer_ctxt() - .enter(|ref infcx| { - let (Eq { param_env, a, b }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); - let fulfill_cx = &mut FulfillmentContext::new(); - let obligations = match infcx.at(&ObligationCause::dummy(), param_env).eq(a, b) { - Ok(v) => v.into_obligations(), - Err(_) => return Err(NoSolution), - }; - fulfill_cx.register_predicate_obligations(infcx, obligations); - infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) - }) - .unwrap() + tcx.type_op_eq(canonicalized).unwrap() } } @@ -79,3 +63,7 @@ BraceStructLiftImpl! { b, } } + +impl_stable_hash_for! { + struct Eq<'tcx> { param_env, a, b } +} diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index cc00e9a00abb3..d02eac6f212ee 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -12,7 +12,9 @@ use dep_graph::SerializedDepNodeIndex; use dep_graph::DepNode; use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId, ConstValue}; -use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal}; +use traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, +}; use ty::{self, ParamEnvAnd, Ty, TyCtxt}; use ty::subst::Substs; use ty::query::queries; @@ -102,6 +104,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::evaluate_obligation<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::type_op_eq<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTypeOpEqGoal<'tcx>) -> String { + format!("evaluating `type_op_eq` `{:?}`", 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/query/keys.rs b/src/librustc/ty/query/keys.rs index 279d5ebb9901e..cad3a6586829e 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc/ty/query/keys.rs @@ -10,8 +10,8 @@ //! Defines the set of legal keys that can be used in queries. +use infer::canonical::Canonical; use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; -use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; @@ -190,27 +190,12 @@ impl Key for InternedString { } } -impl<'tcx> Key for CanonicalProjectionGoal<'tcx> { - fn query_crate(&self) -> CrateNum { - LOCAL_CRATE - } - - fn default_span(&self, _tcx: TyCtxt) -> Span { - DUMMY_SP - } -} - -impl<'tcx> Key for CanonicalTyGoal<'tcx> { - fn query_crate(&self) -> CrateNum { - LOCAL_CRATE - } - - fn default_span(&self, _tcx: TyCtxt) -> Span { - DUMMY_SP - } -} - -impl<'tcx> Key for CanonicalPredicateGoal<'tcx> { +/// Canonical query goals correspond to abstract trait operations that +/// are not tied to any crate in particular. +impl<'tcx, T> Key for Canonical<'tcx, T> +where + T: Debug + Hash + Clone + Eq, +{ fn query_crate(&self) -> CrateNum { LOCAL_CRATE } diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index f19bc01e19857..d5cd37fd98ab0 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -34,7 +34,7 @@ use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::{self, Vtable}; use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, - CanonicalTyGoal, NoSolution}; + CanonicalTyGoal, CanonicalTypeOpEqGoal, NoSolution}; use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; @@ -446,6 +446,14 @@ define_queries! { <'tcx> CanonicalPredicateGoal<'tcx> ) -> Result, + /// Do not call this query directly: invoke `infcx.eq()` instead. + [] fn type_op_eq: TypeOpEq( + CanonicalTypeOpEqGoal<'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/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 4679c265d5805..51695b113d54b 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1028,6 +1028,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::NormalizeTyAfterErasingRegions | DepKind::DropckOutlives | DepKind::EvaluateObligation | + DepKind::TypeOpEq | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | DepKind::ProgramClausesForEnv | diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 830aa93c3c3d1..dae960c9aa37e 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -34,6 +34,7 @@ mod evaluate_obligation; mod normalize_projection_ty; mod normalize_erasing_regions; pub mod lowering; +mod type_op_eq; use rustc::ty::query::Providers; @@ -47,6 +48,7 @@ pub fn provide(p: &mut Providers) { program_clauses_for: lowering::program_clauses_for, program_clauses_for_env: lowering::program_clauses_for_env, evaluate_obligation: evaluate_obligation::evaluate_obligation, + type_op_eq: type_op_eq::type_op_eq, ..*p }; } diff --git a/src/librustc_traits/type_op_eq.rs b/src/librustc_traits/type_op_eq.rs new file mode 100644 index 0000000000000..b73bee4648657 --- /dev/null +++ b/src/librustc_traits/type_op_eq.rs @@ -0,0 +1,35 @@ +// 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::traits::query::type_op::eq::Eq; +use rustc::traits::query::NoSolution; +use rustc::traits::{FulfillmentContext, ObligationCause}; +use rustc::ty::TyCtxt; +use rustc_data_structures::sync::Lrc; +use syntax::codemap::DUMMY_SP; + +crate fn type_op_eq<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Eq<'tcx>>, +) -> Result>>, NoSolution> { + let tcx = tcx.global_tcx(); + tcx.infer_ctxt().enter(|ref infcx| { + let (Eq { param_env, a, b }, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); + let fulfill_cx = &mut FulfillmentContext::new(); + let obligations = match infcx.at(&ObligationCause::dummy(), param_env).eq(a, b) { + Ok(v) => v.into_obligations(), + Err(_) => return Err(NoSolution), + }; + fulfill_cx.register_predicate_obligations(infcx, obligations); + infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) + }) +} From 4beea9943b541e9165bfcbba8b15f9008a7434d1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 10:50:16 -0400 Subject: [PATCH 39/72] make `Subtype` a true query --- src/librustc/dep_graph/dep_node.rs | 4 +- src/librustc/traits/query/mod.rs | 3 ++ src/librustc/traits/query/type_op/subtype.rs | 51 +++++++++++++++----- src/librustc/ty/query/config.rs | 7 +++ src/librustc/ty/query/mod.rs | 10 +++- src/librustc/ty/query/plumbing.rs | 1 + src/librustc_traits/lib.rs | 2 + src/librustc_traits/type_op_subtype.rs | 35 ++++++++++++++ 8 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 src/librustc_traits/type_op_subtype.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index efaebfd06108f..6f0b794129bd4 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -71,7 +71,8 @@ use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; use traits::query::{ - CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalPredicateGoal, + CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, + CanonicalPredicateGoal, }; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; @@ -649,6 +650,7 @@ define_dep_nodes!( <'tcx> [] DropckOutlives(CanonicalTyGoal<'tcx>), [] EvaluateObligation(CanonicalPredicateGoal<'tcx>), [] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>), + [] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 3ee0c0cdd0b6e..47f55b7e5e41a 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -35,6 +35,9 @@ pub type CanonicalPredicateGoal<'tcx> = pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, type_op::eq::Eq<'tcx>>; +pub type CanonicalTypeOpSubtypeGoal<'tcx> = + Canonical<'tcx, type_op::subtype::Subtype<'tcx>>; + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index e9adabc8b0871..01f9386bec460 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -8,15 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferResult}; -use traits::ObligationCause; +use infer::canonical::{Canonical, CanonicalizedQueryResult}; use ty::{ParamEnv, Ty, TyCtxt}; -#[derive(Debug)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Subtype<'tcx> { - param_env: ParamEnv<'tcx>, - sub: Ty<'tcx>, - sup: Ty<'tcx>, + pub param_env: ParamEnv<'tcx>, + pub sub: Ty<'tcx>, + pub sup: Ty<'tcx>, } impl<'tcx> Subtype<'tcx> { @@ -29,10 +28,10 @@ impl<'tcx> Subtype<'tcx> { } } -impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for Subtype<'tcx> { - type Output = (); +impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { + type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self> { if self.sub == self.sup { Ok(()) } else { @@ -40,9 +39,35 @@ impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - infcx - .at(&ObligationCause::dummy(), self.param_env) - .sup(self.sup, self.sub) + fn param_env(&self) -> ParamEnv<'tcx> { + self.param_env } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonical<'gcx, Subtype<'gcx>>, + ) -> CanonicalizedQueryResult<'gcx, ()> { + tcx.type_op_subtype(canonicalized).unwrap() + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for Subtype<'tcx> { + param_env, + sub, + sup, + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for Subtype<'a> { + type Lifted = Subtype<'tcx>; + param_env, + sub, + sup, + } +} + +impl_stable_hash_for! { + struct Subtype<'tcx> { param_env, sub, sup } } diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index d02eac6f212ee..bccdec270998e 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -14,6 +14,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId, ConstValue}; use traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, + CanonicalTypeOpSubtypeGoal, }; use ty::{self, ParamEnvAnd, Ty, TyCtxt}; use ty::subst::Substs; @@ -110,6 +111,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::type_op_eq<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::type_op_subtype<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTypeOpSubtypeGoal<'tcx>) -> String { + format!("evaluating `type_op_eq` `{:?}`", 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/query/mod.rs b/src/librustc/ty/query/mod.rs index d5cd37fd98ab0..e848fa2c347d3 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -34,7 +34,7 @@ use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::{self, Vtable}; use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, - CanonicalTyGoal, CanonicalTypeOpEqGoal, NoSolution}; + CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, NoSolution}; use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; @@ -454,6 +454,14 @@ define_queries! { <'tcx> NoSolution, >, + /// Do not call this query directly: invoke `infcx.at().subtype()` instead. + [] fn type_op_subtype: TypeOpSubtype( + CanonicalTypeOpSubtypeGoal<'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/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 51695b113d54b..2380764252b08 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1029,6 +1029,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::DropckOutlives | DepKind::EvaluateObligation | DepKind::TypeOpEq | + DepKind::TypeOpSubtype | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | DepKind::ProgramClausesForEnv | diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index dae960c9aa37e..671e352c0e4d0 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -35,6 +35,7 @@ mod normalize_projection_ty; mod normalize_erasing_regions; pub mod lowering; mod type_op_eq; +mod type_op_subtype; use rustc::ty::query::Providers; @@ -49,6 +50,7 @@ pub fn provide(p: &mut Providers) { program_clauses_for_env: lowering::program_clauses_for_env, evaluate_obligation: evaluate_obligation::evaluate_obligation, type_op_eq: type_op_eq::type_op_eq, + type_op_subtype: type_op_subtype::type_op_subtype, ..*p }; } diff --git a/src/librustc_traits/type_op_subtype.rs b/src/librustc_traits/type_op_subtype.rs new file mode 100644 index 0000000000000..1fa5ec915f23b --- /dev/null +++ b/src/librustc_traits/type_op_subtype.rs @@ -0,0 +1,35 @@ +// 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::traits::query::type_op::subtype::Subtype; +use rustc::traits::query::NoSolution; +use rustc::traits::{FulfillmentContext, ObligationCause}; +use rustc::ty::TyCtxt; +use rustc_data_structures::sync::Lrc; +use syntax::codemap::DUMMY_SP; + +crate fn type_op_subtype<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Subtype<'tcx>>, +) -> Result>>, NoSolution> { + let tcx = tcx.global_tcx(); + tcx.infer_ctxt().enter(|ref infcx| { + let (Subtype { param_env, sub, sup }, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); + let fulfill_cx = &mut FulfillmentContext::new(); + let obligations = match infcx.at(&ObligationCause::dummy(), param_env).sup(sup, sub) { + Ok(v) => v.into_obligations(), + Err(_) => return Err(NoSolution), + }; + fulfill_cx.register_predicate_obligations(infcx, obligations); + infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) + }) +} From d6136837b770980c8fd271d43358afc455021d9b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 11:09:07 -0400 Subject: [PATCH 40/72] convert `predicates` to operate on 1 predicate at a time --- src/librustc/traits/query/type_op/mod.rs | 2 +- .../{predicates.rs => prove_predicate.rs} | 29 +++++++------------ .../borrow_check/nll/type_check/mod.rs | 29 +++++++------------ 3 files changed, 23 insertions(+), 37 deletions(-) rename src/librustc/traits/query/type_op/{predicates.rs => prove_predicate.rs} (57%) diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 6436e75158ea3..6cda9ea15184a 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -23,7 +23,7 @@ pub mod custom; pub mod eq; pub mod normalize; pub mod outlives; -pub mod predicates; +pub mod prove_predicate; pub mod subtype; pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { diff --git a/src/librustc/traits/query/type_op/predicates.rs b/src/librustc/traits/query/type_op/prove_predicate.rs similarity index 57% rename from src/librustc/traits/query/type_op/predicates.rs rename to src/librustc/traits/query/type_op/prove_predicate.rs index d729ce1e0ebd7..7dacf6a7dea11 100644 --- a/src/librustc/traits/query/type_op/predicates.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -9,43 +9,36 @@ // except according to those terms. use infer::{InferCtxt, InferOk, InferResult}; -use traits::{Obligation, ObligationCause, PredicateObligation}; +use traits::{Obligation, ObligationCause}; use ty::{ParamEnv, Predicate, TyCtxt}; #[derive(Debug)] -pub struct ProvePredicates<'tcx> { - obligations: Vec>, +pub struct ProvePredicate<'tcx> { + param_env: ParamEnv<'tcx>, + predicate: Predicate<'tcx>, } -impl<'tcx> ProvePredicates<'tcx> { +impl<'tcx> ProvePredicate<'tcx> { pub fn new( param_env: ParamEnv<'tcx>, - predicates: impl IntoIterator>, + predicate: Predicate<'tcx>, ) -> Self { - ProvePredicates { - obligations: predicates - .into_iter() - .map(|p| Obligation::new(ObligationCause::dummy(), param_env, p)) - .collect(), - } + ProvePredicate { param_env, predicate } } } -impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for ProvePredicates<'tcx> { +impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { type Output = (); fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if self.obligations.is_empty() { - Ok(()) - } else { - Err(self) - } + Err(self) } fn perform(self, _infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + let obligation = Obligation::new(ObligationCause::dummy(), self.param_env, self.predicate); Ok(InferOk { value: (), - obligations: self.obligations, + obligations: vec![obligation], }) } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 865ee66cccfde..1f45a8ba1739d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1545,26 +1545,19 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { predicates: impl IntoIterator> + Clone, location: Location, ) { - // This intermediate vector is mildly unfortunate, in that we - // sometimes create it even when logging is disabled, but only - // if debug-info is enabled, and I doubt it is actually - // expensive. -nmatsakis - let predicates_vec: Vec<_> = if cfg!(debug_assertions) { - predicates.clone().into_iter().collect() - } else { - Vec::new() - }; - debug!( - "prove_predicates(predicates={:?}, location={:?})", - predicates_vec, location, - ); + for predicate in predicates { + debug!( + "prove_predicates(predicate={:?}, location={:?})", + predicate, location, + ); - let param_env = self.param_env; - self.fully_perform_op( - location.at_self(), - type_op::predicates::ProvePredicates::new(param_env, predicates), - ).unwrap() + let param_env = self.param_env; + self.fully_perform_op( + location.at_self(), + type_op::prove_predicate::ProvePredicate::new(param_env, predicate), + ).unwrap() + } } fn typeck_mir(&mut self, mir: &Mir<'tcx>) { From de7e941e4eafdc73f4d68c6cdbb8c49224e5a90b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 11:29:46 -0400 Subject: [PATCH 41/72] convert `prove_predicate` into a query --- src/librustc/dep_graph/dep_node.rs | 3 +- src/librustc/traits/query/mod.rs | 3 ++ .../traits/query/type_op/prove_predicate.rs | 49 +++++++++++++------ src/librustc/ty/query/config.rs | 8 ++- src/librustc/ty/query/mod.rs | 11 ++++- src/librustc/ty/query/plumbing.rs | 1 + src/librustc_traits/lib.rs | 2 + .../type_op_prove_predicate.rs | 32 ++++++++++++ 8 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 src/librustc_traits/type_op_prove_predicate.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 6f0b794129bd4..94c79c17f0578 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -72,7 +72,7 @@ use std::hash::Hash; use syntax_pos::symbol::InternedString; use traits::query::{ CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, - CanonicalPredicateGoal, + CanonicalPredicateGoal, CanonicalTypeOpProvePredicateGoal, }; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; @@ -651,6 +651,7 @@ define_dep_nodes!( <'tcx> [] EvaluateObligation(CanonicalPredicateGoal<'tcx>), [] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>), [] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>), + [] TypeOpProvePredicate(CanonicalTypeOpProvePredicateGoal<'tcx>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 47f55b7e5e41a..aa0b524af06e5 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -38,6 +38,9 @@ pub type CanonicalTypeOpEqGoal<'tcx> = pub type CanonicalTypeOpSubtypeGoal<'tcx> = Canonical<'tcx, type_op::subtype::Subtype<'tcx>>; +pub type CanonicalTypeOpProvePredicateGoal<'tcx> = + Canonical<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>; + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 7dacf6a7dea11..193dd1c7c841b 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferOk, InferResult}; -use traits::{Obligation, ObligationCause}; +use infer::canonical::{Canonical, CanonicalizedQueryResult}; use ty::{ParamEnv, Predicate, TyCtxt}; -#[derive(Debug)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct ProvePredicate<'tcx> { - param_env: ParamEnv<'tcx>, - predicate: Predicate<'tcx>, + pub param_env: ParamEnv<'tcx>, + pub predicate: Predicate<'tcx>, } impl<'tcx> ProvePredicate<'tcx> { @@ -27,18 +26,40 @@ impl<'tcx> ProvePredicate<'tcx> { } } -impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { - type Output = (); +impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { + type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { Err(self) } - fn perform(self, _infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - let obligation = Obligation::new(ObligationCause::dummy(), self.param_env, self.predicate); - Ok(InferOk { - value: (), - obligations: vec![obligation], - }) + fn param_env(&self) -> ParamEnv<'tcx> { + self.param_env } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonical<'gcx, ProvePredicate<'gcx>>, + ) -> CanonicalizedQueryResult<'gcx, ()> { + tcx.type_op_prove_predicate(canonicalized).unwrap() + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ProvePredicate<'tcx> { + param_env, + predicate, + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for ProvePredicate<'a> { + type Lifted = ProvePredicate<'tcx>; + param_env, + predicate, + } +} + +impl_stable_hash_for! { + struct ProvePredicate<'tcx> { param_env, predicate } } diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index bccdec270998e..930826dad62ee 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -14,7 +14,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId, ConstValue}; use traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, - CanonicalTypeOpSubtypeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, }; use ty::{self, ParamEnvAnd, Ty, TyCtxt}; use ty::subst::Substs; @@ -117,6 +117,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::type_op_subtype<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::type_op_prove_predicate<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTypeOpProvePredicateGoal<'tcx>) -> String { + format!("evaluating `type_op_prove_predicate` `{:?}`", 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/query/mod.rs b/src/librustc/ty/query/mod.rs index e848fa2c347d3..dfc54cb142568 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -34,7 +34,8 @@ use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::{self, Vtable}; use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, - CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, NoSolution}; + CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, + CanonicalTypeOpProvePredicateGoal, NoSolution}; use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; @@ -462,6 +463,14 @@ define_queries! { <'tcx> NoSolution, >, + /// Do not call this query directly: invoke `infcx.at().prove_predicates()` instead. + [] fn type_op_prove_predicate: TypeOpProvePredicate( + CanonicalTypeOpProvePredicateGoal<'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/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 2380764252b08..57568f60b86ee 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1030,6 +1030,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::EvaluateObligation | DepKind::TypeOpEq | DepKind::TypeOpSubtype | + DepKind::TypeOpProvePredicate | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | DepKind::ProgramClausesForEnv | diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 671e352c0e4d0..261d6b2fbfa25 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -35,6 +35,7 @@ mod normalize_projection_ty; mod normalize_erasing_regions; pub mod lowering; mod type_op_eq; +mod type_op_prove_predicate; mod type_op_subtype; use rustc::ty::query::Providers; @@ -50,6 +51,7 @@ pub fn provide(p: &mut Providers) { program_clauses_for_env: lowering::program_clauses_for_env, evaluate_obligation: evaluate_obligation::evaluate_obligation, type_op_eq: type_op_eq::type_op_eq, + type_op_prove_predicate: type_op_prove_predicate::type_op_prove_predicate, type_op_subtype: type_op_subtype::type_op_subtype, ..*p }; diff --git a/src/librustc_traits/type_op_prove_predicate.rs b/src/librustc_traits/type_op_prove_predicate.rs new file mode 100644 index 0000000000000..ad16e6d31d7a0 --- /dev/null +++ b/src/librustc_traits/type_op_prove_predicate.rs @@ -0,0 +1,32 @@ +// 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::traits::query::type_op::prove_predicate::ProvePredicate; +use rustc::traits::query::NoSolution; +use rustc::traits::{FulfillmentContext, Obligation, ObligationCause, TraitEngine}; +use rustc::ty::TyCtxt; +use rustc_data_structures::sync::Lrc; +use syntax::codemap::DUMMY_SP; + +crate fn type_op_prove_predicate<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, ProvePredicate<'tcx>>, +) -> Result>>, NoSolution> { + let tcx = tcx.global_tcx(); + tcx.infer_ctxt().enter(|ref infcx| { + let (ProvePredicate { param_env, predicate }, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); + let fulfill_cx = &mut FulfillmentContext::new(); + let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); + fulfill_cx.register_predicate_obligation(infcx, obligation); + infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) + }) +} From 2a0b3d6224768119f1d2e9850488c3d184362c1c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Jun 2018 11:56:06 -0400 Subject: [PATCH 42/72] introduce `Normalizable` trait for things directly normalizable --- src/librustc/dep_graph/dep_node.rs | 9 +- src/librustc/traits/query/mod.rs | 3 + src/librustc/traits/query/type_op/eq.rs | 8 +- src/librustc/traits/query/type_op/mod.rs | 33 ++-- .../traits/query/type_op/normalize.rs | 151 +++++++++++++++--- .../traits/query/type_op/prove_predicate.rs | 18 ++- src/librustc/traits/query/type_op/subtype.rs | 8 +- src/librustc/ty/query/config.rs | 32 +++- src/librustc/ty/query/mod.rs | 40 ++++- src/librustc/ty/query/plumbing.rs | 4 + .../borrow_check/nll/type_check/mod.rs | 40 +++-- src/librustc_traits/lib.rs | 5 + src/librustc_traits/type_op_normalize.rs | 68 ++++++++ 13 files changed, 362 insertions(+), 57 deletions(-) create mode 100644 src/librustc_traits/type_op_normalize.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 94c79c17f0578..33322993b1db6 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -72,9 +72,10 @@ use std::hash::Hash; use syntax_pos::symbol::InternedString; use traits::query::{ CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, - CanonicalPredicateGoal, CanonicalTypeOpProvePredicateGoal, + CanonicalPredicateGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal, }; -use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; +use ty::{TyCtxt, FnSig, Instance, InstanceDef, + ParamEnv, ParamEnvAnd, Predicate, PolyFnSig, PolyTraitRef, Ty}; use ty::subst::Substs; // erase!() just makes tokens go away. It's used to specify which macro argument @@ -652,6 +653,10 @@ define_dep_nodes!( <'tcx> [] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>), [] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>), [] TypeOpProvePredicate(CanonicalTypeOpProvePredicateGoal<'tcx>), + [] TypeOpNormalizeTy(CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>>), + [] TypeOpNormalizePredicate(CanonicalTypeOpNormalizeGoal<'tcx, Predicate<'tcx>>), + [] TypeOpNormalizePolyFnSig(CanonicalTypeOpNormalizeGoal<'tcx, PolyFnSig<'tcx>>), + [] TypeOpNormalizeFnSig(CanonicalTypeOpNormalizeGoal<'tcx, FnSig<'tcx>>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index aa0b524af06e5..dddd05db668e1 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -41,6 +41,9 @@ pub type CanonicalTypeOpSubtypeGoal<'tcx> = pub type CanonicalTypeOpProvePredicateGoal<'tcx> = Canonical<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>; +pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = + Canonical<'tcx, type_op::normalize::Normalize<'tcx, T>>; + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 8925fa12f03a7..348283d1af344 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{Canonical, CanonicalizedQueryResult}; +use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; use ty::{self, ParamEnv, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -45,6 +45,12 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { ) -> CanonicalizedQueryResult<'gcx, ()> { tcx.type_op_eq(canonicalized).unwrap() } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, ()>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { + v + } } BraceStructTypeFoldableImpl! { diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 6cda9ea15184a..cddb648f04fde 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -9,15 +9,17 @@ // except according to those terms. use infer::canonical::query_result; -use infer::canonical::{Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint}; +use infer::canonical::{ + Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, QueryResult, +}; use infer::{InferCtxt, InferOk, InferResult}; +use std::fmt; +use std::rc::Rc; +use syntax::codemap::DUMMY_SP; use traits::{ObligationCause, TraitEngine}; use ty::error::TypeError; use ty::fold::TypeFoldable; use ty::{Lift, ParamEnv, TyCtxt}; -use std::fmt; -use std::rc::Rc; -use syntax::codemap::DUMMY_SP; pub mod custom; pub mod eq; @@ -98,17 +100,12 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { } } -type Lifted<'gcx, T> = >::Lifted; - pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; /// Micro-optimization: returns `Ok(x)` if we can trivially /// produce the output, else returns `Err(self)` back. - fn trivial_noop( - self, - tcx: TyCtxt<'_, 'gcx, 'tcx>, - ) -> Result, Self>; + fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; fn param_env(&self) -> ParamEnv<'tcx>; @@ -116,14 +113,24 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Self>, ) -> CanonicalizedQueryResult<'gcx, Self::QueryResult>; + + /// "Upcasts" a lifted query result (which is in the gcx lifetime) + /// into the tcx lifetime. This is always just an identity cast, + /// but the generic code does't realize it, so we have to push the + /// operation into the impls that know more specifically what + /// `QueryResult` is. This operation would (maybe) be nicer with + /// something like HKTs or GATs, since then we could make + /// `QueryResult` parametric and `'gcx` and `'tcx` etc. + fn upcast_result( + lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>; } impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for Q where Q: QueryTypeOp<'gcx, 'tcx>, - Lifted<'gcx, Q::QueryResult>: TypeFoldable<'tcx>, { - type Output = Lifted<'gcx, Q::QueryResult>; + type Output = Q::QueryResult; fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { QueryTypeOp::trivial_noop(self, tcx) @@ -152,7 +159,7 @@ where &ObligationCause::dummy(), param_env, &canonical_var_values, - &canonical_result, + Q::upcast_result(&canonical_result), ) } } diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index a363f6f213cb1..d63997fe40c7a 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -8,17 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferOk, InferResult}; -use traits::query::NoSolution; -use traits::{Normalized, ObligationCause}; -use ty::fold::TypeFoldable; -use ty::{ParamEnv, TyCtxt}; +use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; use std::fmt; +use ty::fold::TypeFoldable; +use ty::{self, Lift, ParamEnv, Ty, TyCtxt}; -#[derive(Debug)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Normalize<'tcx, T> { - param_env: ParamEnv<'tcx>, - value: T, + pub param_env: ParamEnv<'tcx>, + pub value: T, } impl<'tcx, T> Normalize<'tcx, T> @@ -30,13 +28,13 @@ where } } -impl<'gcx, 'tcx, T> super::TypeOp<'gcx, 'tcx> for Normalize<'tcx, T> +impl<'gcx: 'tcx, 'tcx, T> super::QueryTypeOp<'gcx, 'tcx> for Normalize<'tcx, T> where - T: fmt::Debug + TypeFoldable<'tcx>, + T: Normalizable<'gcx, 'tcx>, { - type Output = T; + type QueryResult = T; - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if !self.value.has_projections() { Ok(self.value) } else { @@ -44,13 +42,126 @@ where } } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { - let Normalized { value, obligations } = infcx - .at(&ObligationCause::dummy(), self.param_env) - .normalize(&self.value) - .unwrap_or_else(|NoSolution| { - bug!("normalization of `{:?}` failed", self.value,); - }); - Ok(InferOk { value, obligations }) + fn param_env(&self) -> ParamEnv<'tcx> { + self.param_env + } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Self>, + ) -> CanonicalizedQueryResult<'gcx, Self::QueryResult> { + T::type_op_method(tcx, canonicalized) + } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, T>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, T>> { + T::upcast_result(v) + } +} + +pub trait Normalizable<'gcx, 'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx> { + fn type_op_method( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + ) -> CanonicalizedQueryResult<'gcx, Self>; + + /// Convert from the `'gcx` (lifted) form of `Self` into the `tcx` + /// form of `Self`. + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, Self>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>>; +} + +impl Normalizable<'gcx, 'tcx> for Ty<'tcx> +where + 'gcx: 'tcx, +{ + fn type_op_method( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + ) -> CanonicalizedQueryResult<'gcx, Self> { + tcx.type_op_normalize_ty(canonicalized).unwrap() + } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, Self>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { + v + } +} + +impl Normalizable<'gcx, 'tcx> for ty::Predicate<'tcx> +where + 'gcx: 'tcx, +{ + fn type_op_method( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + ) -> CanonicalizedQueryResult<'gcx, Self> { + tcx.type_op_normalize_predicate(canonicalized).unwrap() + } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, Self>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { + v + } +} + +impl Normalizable<'gcx, 'tcx> for ty::PolyFnSig<'tcx> +where + 'gcx: 'tcx, +{ + fn type_op_method( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + ) -> CanonicalizedQueryResult<'gcx, Self> { + tcx.type_op_normalize_poly_fn_sig(canonicalized).unwrap() + } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, Self>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { + v + } +} + +impl Normalizable<'gcx, 'tcx> for ty::FnSig<'tcx> +where + 'gcx: 'tcx, +{ + fn type_op_method( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + ) -> CanonicalizedQueryResult<'gcx, Self> { + tcx.type_op_normalize_fn_sig(canonicalized).unwrap() + } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, Self>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { + v + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for Normalize<'tcx, T> { + param_env, + value, + } where T: TypeFoldable<'tcx>, +} + +BraceStructLiftImpl! { + impl<'a, 'tcx, T> Lift<'tcx> for Normalize<'a, T> { + type Lifted = Normalize<'tcx, T::Lifted>; + param_env, + value, + } where T: Lift<'tcx>, +} + +impl_stable_hash_for! { + impl<'tcx, T> for struct Normalize<'tcx, T> { + param_env, value } } diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 193dd1c7c841b..866ebd0cc12e1 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{Canonical, CanonicalizedQueryResult}; +use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; use ty::{ParamEnv, Predicate, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -18,11 +18,11 @@ pub struct ProvePredicate<'tcx> { } impl<'tcx> ProvePredicate<'tcx> { - pub fn new( - param_env: ParamEnv<'tcx>, - predicate: Predicate<'tcx>, - ) -> Self { - ProvePredicate { param_env, predicate } + pub fn new(param_env: ParamEnv<'tcx>, predicate: Predicate<'tcx>) -> Self { + ProvePredicate { + param_env, + predicate, + } } } @@ -43,6 +43,12 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { ) -> CanonicalizedQueryResult<'gcx, ()> { tcx.type_op_prove_predicate(canonicalized).unwrap() } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, ()>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { + v + } } BraceStructTypeFoldableImpl! { diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index 01f9386bec460..a0fb2c2763da1 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{Canonical, CanonicalizedQueryResult}; +use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; use ty::{ParamEnv, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -49,6 +49,12 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { ) -> CanonicalizedQueryResult<'gcx, ()> { tcx.type_op_subtype(canonicalized).unwrap() } + + fn upcast_result( + v: &'a CanonicalizedQueryResult<'gcx, ()>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { + v + } } BraceStructTypeFoldableImpl! { diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index 930826dad62ee..8a48abd093d7f 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -14,7 +14,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId, ConstValue}; use traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, + CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, }; use ty::{self, ParamEnvAnd, Ty, TyCtxt}; use ty::subst::Substs; @@ -123,6 +123,36 @@ impl<'tcx> QueryDescription<'tcx> for queries::type_op_prove_predicate<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::type_op_normalize_ty<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>>) -> String { + format!("normalizing `{:?}`", goal) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::type_op_normalize_predicate<'tcx> { + fn describe( + _tcx: TyCtxt, + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>>, + ) -> String { + format!("normalizing `{:?}`", goal) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::type_op_normalize_poly_fn_sig<'tcx> { + fn describe( + _tcx: TyCtxt, + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>>, + ) -> String { + format!("normalizing `{:?}`", goal) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::type_op_normalize_fn_sig<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>>) -> String { + format!("normalizing `{:?}`", 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/query/mod.rs b/src/librustc/ty/query/mod.rs index dfc54cb142568..178ee7cf8e9ac 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -35,7 +35,7 @@ use session::config::OutputFilenames; use traits::{self, Vtable}; use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, - CanonicalTypeOpProvePredicateGoal, NoSolution}; + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal, NoSolution}; use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; @@ -447,7 +447,7 @@ define_queries! { <'tcx> CanonicalPredicateGoal<'tcx> ) -> Result, - /// Do not call this query directly: invoke `infcx.eq()` instead. + /// Do not call this query directly: part of the `Eq` type-op [] fn type_op_eq: TypeOpEq( CanonicalTypeOpEqGoal<'tcx> ) -> Result< @@ -455,7 +455,7 @@ define_queries! { <'tcx> NoSolution, >, - /// Do not call this query directly: invoke `infcx.at().subtype()` instead. + /// Do not call this query directly: part of the `Subtype` type-op [] fn type_op_subtype: TypeOpSubtype( CanonicalTypeOpSubtypeGoal<'tcx> ) -> Result< @@ -463,7 +463,7 @@ define_queries! { <'tcx> NoSolution, >, - /// Do not call this query directly: invoke `infcx.at().prove_predicates()` instead. + /// Do not call this query directly: part of the `ProvePredicate` type-op [] fn type_op_prove_predicate: TypeOpProvePredicate( CanonicalTypeOpProvePredicateGoal<'tcx> ) -> Result< @@ -471,6 +471,38 @@ define_queries! { <'tcx> NoSolution, >, + /// Do not call this query directly: part of the `Normalize` type-op + [] fn type_op_normalize_ty: TypeOpNormalizeTy( + CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> + ) -> Result< + Lrc>>>, + NoSolution, + >, + + /// Do not call this query directly: part of the `Normalize` type-op + [] fn type_op_normalize_predicate: TypeOpNormalizePredicate( + CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> + ) -> Result< + Lrc>>>, + NoSolution, + >, + + /// Do not call this query directly: part of the `Normalize` type-op + [] fn type_op_normalize_poly_fn_sig: TypeOpNormalizePolyFnSig( + CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> + ) -> Result< + Lrc>>>, + NoSolution, + >, + + /// Do not call this query directly: part of the `Normalize` type-op + [] fn type_op_normalize_fn_sig: TypeOpNormalizeFnSig( + CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'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/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 57568f60b86ee..e17c6fba74c6e 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1031,6 +1031,10 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::TypeOpEq | DepKind::TypeOpSubtype | DepKind::TypeOpProvePredicate | + DepKind::TypeOpNormalizeTy | + DepKind::TypeOpNormalizePredicate | + DepKind::TypeOpNormalizePolyFnSig | + DepKind::TypeOpNormalizeFnSig | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | DepKind::ProgramClausesForEnv | diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 1f45a8ba1739d..d8818b704bbe8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -286,9 +286,10 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); - let predicates = - type_checker.normalize(instantiated_predicates.predicates, location); - type_checker.prove_predicates(predicates, location); + type_checker.normalize_and_prove_instantiated_predicates( + instantiated_predicates, + location, + ); } value.ty @@ -1526,9 +1527,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), }; - let predicates = self.normalize(instantiated_predicates.predicates, location); - debug!("prove_aggregate_predicates: predicates={:?}", predicates); - self.prove_predicates(predicates, location); + self.normalize_and_prove_instantiated_predicates(instantiated_predicates, location); } fn prove_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>, location: Location) { @@ -1540,12 +1539,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); } - fn prove_predicates( + fn normalize_and_prove_instantiated_predicates( &mut self, - predicates: impl IntoIterator> + Clone, + instantiated_predicates: ty::InstantiatedPredicates<'tcx>, location: Location, ) { + for predicate in instantiated_predicates.predicates { + let predicate = self.normalize(predicate, location); + self.prove_predicate(predicate, location); + } + } + fn prove_predicates( + &mut self, + predicates: impl IntoIterator>, + location: Location, + ) { for predicate in predicates { debug!( "prove_predicates(predicate={:?}, location={:?})", @@ -1560,6 +1569,19 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } + fn prove_predicate(&mut self, predicate: ty::Predicate<'tcx>, location: Location) { + debug!( + "prove_predicate(predicate={:?}, location={:?})", + predicate, location, + ); + + let param_env = self.param_env; + self.fully_perform_op( + location.at_self(), + type_op::prove_predicate::ProvePredicate::new(param_env, predicate), + ).unwrap() + } + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); @@ -1588,7 +1610,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn normalize(&mut self, value: T, location: impl ToLocations) -> T where - T: fmt::Debug + TypeFoldable<'tcx>, + T: type_op::normalize::Normalizable<'gcx, 'tcx>, { debug!("normalize(value={:?}, location={:?})", value, location); let param_env = self.param_env; diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 261d6b2fbfa25..f81b6a2d906f9 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -35,6 +35,7 @@ mod normalize_projection_ty; mod normalize_erasing_regions; pub mod lowering; mod type_op_eq; +mod type_op_normalize; mod type_op_prove_predicate; mod type_op_subtype; @@ -53,6 +54,10 @@ pub fn provide(p: &mut Providers) { type_op_eq: type_op_eq::type_op_eq, type_op_prove_predicate: type_op_prove_predicate::type_op_prove_predicate, type_op_subtype: type_op_subtype::type_op_subtype, + type_op_normalize_ty: type_op_normalize::type_op_normalize_ty, + type_op_normalize_predicate: type_op_normalize::type_op_normalize_predicate, + type_op_normalize_fn_sig: type_op_normalize::type_op_normalize_fn_sig, + type_op_normalize_poly_fn_sig: type_op_normalize::type_op_normalize_poly_fn_sig, ..*p }; } diff --git a/src/librustc_traits/type_op_normalize.rs b/src/librustc_traits/type_op_normalize.rs new file mode 100644 index 0000000000000..edfe627b15a3a --- /dev/null +++ b/src/librustc_traits/type_op_normalize.rs @@ -0,0 +1,68 @@ +// 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::infer::InferCtxt; +use rustc::traits::query::type_op::normalize::Normalize; +use rustc::traits::query::NoSolution; +use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; +use rustc::ty::{FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; +use rustc_data_structures::sync::Lrc; +use std::fmt; +use syntax::codemap::DUMMY_SP; + +fn type_op_normalize<'gcx, 'tcx, T>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonical<'tcx, Normalize<'tcx, T>>, +) -> Result>::Lifted>>>, NoSolution> +where + T: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx>, +{ + let (Normalize { param_env, value }, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); + let fulfill_cx = &mut FulfillmentContext::new(); + let Normalized { value, obligations } = infcx + .at(&ObligationCause::dummy(), param_env) + .normalize(&value)?; + fulfill_cx.register_predicate_obligations(infcx, obligations); + infcx.make_canonicalized_query_result(canonical_inference_vars, value, fulfill_cx) +} + +crate fn type_op_normalize_ty<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Normalize<'tcx, Ty<'tcx>>>, +) -> Result>>>, NoSolution> { + tcx.infer_ctxt() + .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) +} + +crate fn type_op_normalize_predicate<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Normalize<'tcx, Predicate<'tcx>>>, +) -> Result>>>, NoSolution> { + tcx.infer_ctxt() + .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) +} + +crate fn type_op_normalize_fn_sig<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Normalize<'tcx, FnSig<'tcx>>>, +) -> Result>>>, NoSolution> { + tcx.infer_ctxt() + .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) +} + +crate fn type_op_normalize_poly_fn_sig<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Normalize<'tcx, PolyFnSig<'tcx>>>, +) -> Result>>>, NoSolution> { + tcx.infer_ctxt() + .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) +} From 3e32d42532c9499066c1f45f8a301c2a81e45ec8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 12 Jun 2018 14:33:12 -0400 Subject: [PATCH 43/72] transition to `Fallible` --- src/librustc/traits/query/mod.rs | 7 +++++ src/librustc/traits/query/type_op/custom.rs | 11 ++++---- src/librustc/traits/query/type_op/eq.rs | 5 ++-- src/librustc/traits/query/type_op/mod.rs | 26 +++++++++++------- .../traits/query/type_op/normalize.rs | 21 ++++++++------- src/librustc/traits/query/type_op/outlives.rs | 8 ++++-- .../traits/query/type_op/prove_predicate.rs | 5 ++-- src/librustc/traits/query/type_op/subtype.rs | 5 ++-- .../borrow_check/nll/type_check/mod.rs | 27 ++++++++++--------- 9 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index dddd05db668e1..96034dd935ca8 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -16,6 +16,7 @@ //! `librustc_traits`. use infer::canonical::Canonical; +use ty::error::TypeError; use ty::{self, Ty}; pub mod dropck_outlives; @@ -49,4 +50,10 @@ pub struct NoSolution; pub type Fallible = Result; +impl<'tcx> From> for NoSolution { + fn from(_: TypeError<'tcx>) -> NoSolution { + NoSolution + } +} + impl_stable_hash_for!(struct NoSolution { }); diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 65b2ab31eba4c..d3bc81a09a995 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferResult}; +use infer::{InferCtxt, InferOk}; +use traits::query::Fallible; use ty::TyCtxt; use std::fmt; @@ -20,7 +21,7 @@ pub struct CustomTypeOp { impl CustomTypeOp { pub fn new<'gcx, 'tcx, R>(closure: F, description: G) -> Self where - F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + F: FnOnce(&InferCtxt<'_, 'gcx, 'tcx>) -> Fallible>, G: Fn() -> String, { CustomTypeOp { @@ -32,7 +33,7 @@ impl CustomTypeOp { impl<'gcx, 'tcx, F, R, G> super::TypeOp<'gcx, 'tcx> for CustomTypeOp where - F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'gcx, 'tcx>) -> InferResult<'tcx, R>, + F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'gcx, 'tcx>) -> Fallible>, G: Fn() -> String, { type Output = R; @@ -41,8 +42,8 @@ where Err(self) } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, R> { - (self.closure)(infcx) + fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> Fallible> { + Ok((self.closure)(infcx)?) } } diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 348283d1af344..cc5f721518411 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -9,6 +9,7 @@ // except according to those terms. use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; +use traits::query::Fallible; use ty::{self, ParamEnv, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -42,8 +43,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonical<'gcx, Eq<'gcx>>, - ) -> CanonicalizedQueryResult<'gcx, ()> { - tcx.type_op_eq(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_eq(canonicalized) } fn upcast_result( diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index cddb648f04fde..4addb6c027b95 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -12,12 +12,12 @@ use infer::canonical::query_result; use infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, QueryResult, }; -use infer::{InferCtxt, InferOk, InferResult}; +use infer::{InferCtxt, InferOk}; use std::fmt; use std::rc::Rc; use syntax::codemap::DUMMY_SP; +use traits::query::Fallible; use traits::{ObligationCause, TraitEngine}; -use ty::error::TypeError; use ty::fold::TypeFoldable; use ty::{Lift, ParamEnv, TyCtxt}; @@ -42,7 +42,10 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { /// should use `fully_perform`, which will take those resulting /// obligations and prove them, and then process the combined /// results into region obligations which are returned. - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output>; + fn perform( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ) -> Fallible>; /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints @@ -50,7 +53,7 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Result<(Self::Output, Option>>>), TypeError<'tcx>> { + ) -> Fallible<(Self::Output, Option>>>)> { match self.trivial_noop(infcx.tcx) { Ok(r) => Ok((r, None)), Err(op) => op.fully_perform_nontrivial(infcx), @@ -62,7 +65,7 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { fn fully_perform_nontrivial( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Result<(Self::Output, Option>>>), TypeError<'tcx>> { + ) -> Fallible<(Self::Output, Option>>>)> { if cfg!(debug_assertions) { info!( "fully_perform_op_and_get_region_constraint_data({:?})", @@ -112,7 +115,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Self>, - ) -> CanonicalizedQueryResult<'gcx, Self::QueryResult>; + ) -> Fallible>; /// "Upcasts" a lifted query result (which is in the gcx lifetime) /// into the tcx lifetime. This is always just an identity cast, @@ -136,7 +139,10 @@ where QueryTypeOp::trivial_noop(self, tcx) } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + fn perform( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ) -> Fallible> { let param_env = self.param_env(); // FIXME(#33684) -- We need to use @@ -144,7 +150,7 @@ where // the subtype query, which go awry around `'static` // otherwise. let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&self); - let canonical_result = Q::perform_query(infcx.tcx, canonical_self); + let canonical_result = Q::perform_query(infcx.tcx, canonical_self)?; // FIXME: This is not the most efficient setup. The // `instantiate_query_result_and_region_obligations` basically @@ -155,11 +161,11 @@ where // `QueryRegionConstraint` and ultimately into NLL // constraints. We should cut out the middleman but that will // take a bit of refactoring. - infcx.instantiate_query_result_and_region_obligations( + Ok(infcx.instantiate_query_result_and_region_obligations( &ObligationCause::dummy(), param_env, &canonical_var_values, Q::upcast_result(&canonical_result), - ) + )?) } } diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index d63997fe40c7a..80bcc20f842c8 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -10,6 +10,7 @@ use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; use std::fmt; +use traits::query::Fallible; use ty::fold::TypeFoldable; use ty::{self, Lift, ParamEnv, Ty, TyCtxt}; @@ -49,7 +50,7 @@ where fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Self>, - ) -> CanonicalizedQueryResult<'gcx, Self::QueryResult> { + ) -> Fallible> { T::type_op_method(tcx, canonicalized) } @@ -64,7 +65,7 @@ pub trait Normalizable<'gcx, 'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx> fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, - ) -> CanonicalizedQueryResult<'gcx, Self>; + ) -> Fallible>; /// Convert from the `'gcx` (lifted) form of `Self` into the `tcx` /// form of `Self`. @@ -80,8 +81,8 @@ where fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, - ) -> CanonicalizedQueryResult<'gcx, Self> { - tcx.type_op_normalize_ty(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_normalize_ty(canonicalized) } fn upcast_result( @@ -98,8 +99,8 @@ where fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, - ) -> CanonicalizedQueryResult<'gcx, Self> { - tcx.type_op_normalize_predicate(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_normalize_predicate(canonicalized) } fn upcast_result( @@ -116,8 +117,8 @@ where fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, - ) -> CanonicalizedQueryResult<'gcx, Self> { - tcx.type_op_normalize_poly_fn_sig(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_normalize_poly_fn_sig(canonicalized) } fn upcast_result( @@ -134,8 +135,8 @@ where fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, - ) -> CanonicalizedQueryResult<'gcx, Self> { - tcx.type_op_normalize_fn_sig(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_normalize_fn_sig(canonicalized) } fn upcast_result( diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index f6c60bcaf38cd..419146d82ec4d 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -8,8 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferResult}; +use infer::{InferCtxt, InferOk}; use traits::query::dropck_outlives::trivial_dropck_outlives; +use traits::query::Fallible; use traits::ObligationCause; use ty::subst::Kind; use ty::{ParamEnv, Ty, TyCtxt}; @@ -40,7 +41,10 @@ impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { } } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> InferResult<'tcx, Self::Output> { + fn perform( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ) -> Fallible>>> { Ok(infcx .at(&ObligationCause::dummy(), self.param_env) .dropck_outlives(self.dropped_ty)) diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 866ebd0cc12e1..d2714803eaecc 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -9,6 +9,7 @@ // except according to those terms. use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; +use traits::query::Fallible; use ty::{ParamEnv, Predicate, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -40,8 +41,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonical<'gcx, ProvePredicate<'gcx>>, - ) -> CanonicalizedQueryResult<'gcx, ()> { - tcx.type_op_prove_predicate(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_prove_predicate(canonicalized) } fn upcast_result( diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index a0fb2c2763da1..7bb1eda4eecfe 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -9,6 +9,7 @@ // except according to those terms. use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; +use traits::query::Fallible; use ty::{ParamEnv, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -46,8 +47,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonical<'gcx, Subtype<'gcx>>, - ) -> CanonicalizedQueryResult<'gcx, ()> { - tcx.type_op_subtype(canonicalized).unwrap() + ) -> Fallible> { + tcx.type_op_subtype(canonicalized) } fn upcast_result( diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d8818b704bbe8..7a8337b2498db 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -22,14 +22,14 @@ use dataflow::MaybeInitializedPlaces; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::region_constraints::GenericKind; -use rustc::infer::{InferCtxt, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::{InferCtxt, LateBoundRegionConversionTime}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; use rustc::traits::query::type_op; +use rustc::traits::query::{Fallible, NoSolution}; use rustc::traits::ObligationCause; -use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use std::fmt; @@ -733,7 +733,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { &mut self, locations: Locations, op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>, - ) -> Result> { + ) -> Fallible { let (r, opt_data) = op.fully_perform(self.infcx)?; if let Some(data) = &opt_data { @@ -775,7 +775,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { sub: Ty<'tcx>, sup: Ty<'tcx>, locations: Locations, - ) -> UnitResult<'tcx> { + ) -> Fallible<()> { let param_env = self.param_env; self.fully_perform_op( locations, @@ -783,7 +783,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ) } - fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { + fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> { let param_env = self.param_env; self.fully_perform_op(locations, type_op::eq::Eq::new(param_env, b, a)) } @@ -1561,11 +1561,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { predicate, location, ); - let param_env = self.param_env; - self.fully_perform_op( - location.at_self(), - type_op::prove_predicate::ProvePredicate::new(param_env, predicate), - ).unwrap() + self.prove_predicate(predicate, location); } } @@ -1579,7 +1575,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.fully_perform_op( location.at_self(), type_op::prove_predicate::ProvePredicate::new(param_env, predicate), - ).unwrap() + ).unwrap_or_else(|NoSolution| { + span_mirbug!(self, NoSolution, "could not prove {:?}", predicate); + }) } fn typeck_mir(&mut self, mir: &Mir<'tcx>) { @@ -1610,14 +1608,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn normalize(&mut self, value: T, location: impl ToLocations) -> T where - T: type_op::normalize::Normalizable<'gcx, 'tcx>, + T: type_op::normalize::Normalizable<'gcx, 'tcx> + Copy, { debug!("normalize(value={:?}, location={:?})", value, location); let param_env = self.param_env; self.fully_perform_op( location.to_locations(), type_op::normalize::Normalize::new(param_env, value), - ).unwrap() + ).unwrap_or_else(|NoSolution| { + span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value); + value + }) } } From 7c72e778ab3cfa0dc7dc2226952c6b9f1e1c76b6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Jun 2018 04:42:21 -0400 Subject: [PATCH 44/72] instantiate closure requirements as query-region-constraints [WIP] Marked as WIP because it invalidates some tests. --- .../borrow_check/nll/region_infer/mod.rs | 90 ++++++++----------- .../borrow_check/nll/type_check/mod.rs | 12 +-- .../borrow_check/nll/universal_regions.rs | 6 +- 3 files changed, 48 insertions(+), 60 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 0eeacda467e03..2e1f7fc9e7007 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -11,25 +11,22 @@ use super::universal_regions::UniversalRegions; use borrow_check::nll::region_infer::values::ToElementIndex; use rustc::hir::def_id::DefId; +use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::region_constraints::{GenericKind, VarInfos}; use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; -use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; -use rustc::infer::SubregionOrigin; use rustc::mir::{ ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location, Mir, }; -use rustc::traits::ObligationCause; -use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; +use rustc::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use std::fmt; use std::rc::Rc; -use syntax::ast; use syntax_pos::Span; mod annotation; @@ -1162,16 +1159,15 @@ impl fmt::Debug for OutlivesConstraint { pub trait ClosureRegionRequirementsExt<'gcx, 'tcx> { fn apply_requirements( &self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - body_id: ast::NodeId, + tcx: TyCtxt<'_, 'gcx, 'tcx>, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, - ); + ) -> Vec>; fn subst_closure_mapping( &self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, + tcx: TyCtxt<'_, 'gcx, 'tcx>, closure_mapping: &IndexVec>, value: &T, ) -> T @@ -1194,14 +1190,11 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi /// requirements. fn apply_requirements( &self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - body_id: ast::NodeId, + tcx: TyCtxt<'_, 'gcx, 'tcx>, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, - ) { - let tcx = infcx.tcx; - + ) -> Vec> { debug!( "apply_requirements(location={:?}, closure_def_id={:?}, closure_substs={:?})", location, closure_def_id, closure_substs @@ -1215,59 +1208,52 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi // into a vector. These are the regions that we will be // relating to one another. let closure_mapping = - &UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids); + &UniversalRegions::closure_mapping(tcx, user_closure_ty, self.num_external_vids); debug!("apply_requirements: closure_mapping={:?}", closure_mapping); // Create the predicates. - for outlives_requirement in &self.outlives_requirements { - let outlived_region = closure_mapping[outlives_requirement.outlived_free_region]; - - // FIXME, this origin is not entirely suitable. - let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span); - - match outlives_requirement.subject { - ClosureOutlivesSubject::Region(region) => { - let region = closure_mapping[region]; - debug!( - "apply_requirements: region={:?} \ - outlived_region={:?} \ - outlives_requirement={:?}", - region, outlived_region, outlives_requirement, - ); - infcx.sub_regions(origin, outlived_region, region); - } + self.outlives_requirements + .iter() + .map(|outlives_requirement| { + let outlived_region = closure_mapping[outlives_requirement.outlived_free_region]; + + match outlives_requirement.subject { + ClosureOutlivesSubject::Region(region) => { + let region = closure_mapping[region]; + debug!( + "apply_requirements: region={:?} \ + outlived_region={:?} \ + outlives_requirement={:?}", + region, outlived_region, outlives_requirement, + ); + ty::Binder::dummy(ty::OutlivesPredicate(region.into(), outlived_region)) + } - ClosureOutlivesSubject::Ty(ty) => { - let ty = self.subst_closure_mapping(infcx, closure_mapping, &ty); - debug!( - "apply_requirements: ty={:?} \ - outlived_region={:?} \ - outlives_requirement={:?}", - ty, outlived_region, outlives_requirement, - ); - infcx.register_region_obligation( - body_id, - RegionObligation { - sup_type: ty, - sub_region: outlived_region, - cause: ObligationCause::misc(outlives_requirement.blame_span, body_id), - }, - ); + ClosureOutlivesSubject::Ty(ty) => { + let ty = self.subst_closure_mapping(tcx, closure_mapping, &ty); + debug!( + "apply_requirements: ty={:?} \ + outlived_region={:?} \ + outlives_requirement={:?}", + ty, outlived_region, outlives_requirement, + ); + ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region)) + } } - } - } + }) + .collect() } fn subst_closure_mapping( &self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, + tcx: TyCtxt<'_, 'gcx, 'tcx>, closure_mapping: &IndexVec>, value: &T, ) -> T where T: TypeFoldable<'tcx>, { - infcx.tcx.fold_regions(value, &mut false, |r, _depth| { + tcx.fold_regions(value, &mut false, |r, _depth| { if let ty::ReClosureBound(vid) = r { closure_mapping[*vid] } else { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 7a8337b2498db..843fadf62272b 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -29,7 +29,6 @@ use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; use rustc::traits::query::type_op; use rustc::traits::query::{Fallible, NoSolution}; -use rustc::traits::ObligationCause; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use std::fmt; @@ -1507,14 +1506,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id).closure_requirements { - let dummy_body_id = ObligationCause::dummy().body_id; - closure_region_requirements.apply_requirements( - self.infcx, - dummy_body_id, + let closure_constraints = closure_region_requirements.apply_requirements( + self.infcx.tcx, location, *def_id, *substs, ); + + self.push_region_constraints( + location.at_self(), + &closure_constraints, + ); } tcx.predicates_of(*def_id).instantiate(tcx, substs.substs) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 2bb96a856ce6d..ec8cd386679c3 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -238,13 +238,13 @@ impl<'tcx> UniversalRegions<'tcx> { /// `'1: '2`, then the caller would impose the constraint that /// `V[1]: V[2]`. pub fn closure_mapping( - infcx: &InferCtxt<'_, '_, 'tcx>, + tcx: TyCtxt<'_, '_, 'tcx>, closure_ty: Ty<'tcx>, expected_num_vars: usize, ) -> IndexVec> { let mut region_mapping = IndexVec::with_capacity(expected_num_vars); - region_mapping.push(infcx.tcx.types.re_static); - infcx.tcx.for_each_free_region(&closure_ty, |fr| { + region_mapping.push(tcx.types.re_static); + tcx.for_each_free_region(&closure_ty, |fr| { region_mapping.push(fr); }); From 7bab9f09749f497ef3a8dd1a4961f2813e9a9d11 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 20 Jun 2018 16:04:34 -0400 Subject: [PATCH 45/72] WIP fix error messages for propagate_approximated_shorter_to_static_no_bound --- ...ropagate-approximated-shorter-to-static-no-bound.stderr | 7 ++++--- ...agate-approximated-shorter-to-static-wrong-bound.stderr | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 93a7c1a0c6c33..122e393f97a53 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -24,15 +24,16 @@ LL | | }); = note: where '_#1r: '_#0r error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic` - --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:5 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47 | -LL | / establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | _______________________________________________^ LL | | //~^ ERROR does not outlive free region LL | | LL | | // Only works if 'x: 'y: LL | | demand_y(x, y, x.get()) //~ WARNING not reporting region error due to nll LL | | }); - | |______^ + | |_____^ note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index c62f62efda361..8cdbc26458150 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -24,15 +24,16 @@ LL | | }); = note: where '_#1r: '_#0r error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic` - --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:5 + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:47 | -LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + | _______________________________________________^ LL | | //~^ ERROR does not outlive free region LL | | // Only works if 'x: 'y: LL | | demand_y(x, y, x.get()) LL | | //~^ WARNING not reporting region error due to nll LL | | }); - | |______^ + | |_____^ note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:47:1 From f24e90ec25c066578b7ff33bbc27331e31649a14 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Jun 2018 05:08:24 -0400 Subject: [PATCH 46/72] extract more helpers from instantiating query result --- src/librustc/infer/canonical/query_result.rs | 103 ++++++++++++++----- 1 file changed, 79 insertions(+), 24 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 3fc68994e2197..71cefda6b6646 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -19,8 +19,8 @@ use infer::canonical::substitute::substitute_value; use infer::canonical::{ - Canonical, CanonicalVarValues, CanonicalizedQueryResult, Certainty, - QueryRegionConstraint, QueryResult, + Canonical, CanonicalVarValues, CanonicalizedQueryResult, Certainty, QueryRegionConstraint, + QueryResult, }; use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; @@ -155,12 +155,10 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { where R: Debug + TypeFoldable<'tcx>, { - let InferOk { value: result_subst, mut obligations } = self.query_result_substitution( - cause, - param_env, - original_values, - query_result, - )?; + let InferOk { + value: result_subst, + mut obligations, + } = self.query_result_substitution(cause, param_env, original_values, query_result)?; obligations.extend(self.query_region_constraints_into_obligations( cause, @@ -188,7 +186,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// example) we are doing lazy normalization and the value /// assigned to a type variable is unified with an unnormalized /// projection. - pub fn query_result_substitution( + fn query_result_substitution( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -199,7 +197,49 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { R: Debug + TypeFoldable<'tcx>, { debug!( - "instantiate_query_result(original_values={:#?}, query_result={:#?})", + "query_result_substitution(original_values={:#?}, query_result={:#?})", + original_values, query_result, + ); + + let result_subst = + self.query_result_substitution_guess(cause, original_values, query_result); + + let obligations = self + .unify_query_result_substitution_guess( + cause, + param_env, + original_values, + &result_subst, + query_result, + )? + .into_obligations(); + + Ok(InferOk { + value: result_subst, + obligations, + }) + } + + /// Given the original values and the (canonicalized) result from + /// computing a query, returns a **guess** at a substitution that + /// can be applied to the query result to convert the result back + /// into the original namespace. This is called a **guess** + /// because it uses a quick heuristic to find the values for each + /// canonical variable; if that quick heuristic fails, then we + /// will instantiate fresh inference variables for each canonical + /// variable instead. Therefore, the result of this method must be + /// properly unified + fn query_result_substitution_guess( + &self, + cause: &ObligationCause<'tcx>, + original_values: &CanonicalVarValues<'tcx>, + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, + ) -> CanonicalVarValues<'tcx> + where + R: Debug + TypeFoldable<'tcx>, + { + debug!( + "query_result_substitution_guess(original_values={:#?}, query_result={:#?})", original_values, query_result, ); @@ -256,22 +296,37 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .collect(), }; - // Unify the original values for the canonical variables in - // the input with the value found in the query - // post-substitution. Often, but not always, this is a no-op, - // because we already found the mapping in the first step. - let obligations = { - let substituted_values = |index: CanonicalVar| -> Kind<'tcx> { - query_result.substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]) - }; - self.unify_canonical_vars(cause, param_env, original_values, substituted_values)? - .into_obligations() + result_subst + } + + /// Given a "guess" at the values for the canonical variables in + /// the input, try to unify with the *actual* values found in the + /// query result. Often, but not always, this is a no-op, because + /// we already found the mapping in the "guessing" step. + /// + /// See also: `query_result_substitution_guess` + fn unify_query_result_substitution_guess( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &CanonicalVarValues<'tcx>, + result_subst: &CanonicalVarValues<'tcx>, + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, + ) -> InferResult<'tcx, ()> + where + R: Debug + TypeFoldable<'tcx>, + { + // A closure that yields the result value for the given + // canonical variable; this is taken from + // `query_result.var_values` after applying the substitution + // `result_subst`. + let substituted_query_result = |index: CanonicalVar| -> Kind<'tcx> { + query_result.substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]) }; - Ok(InferOk { - value: result_subst, - obligations, - }) + // Unify the original value for each variable with the value + // taken from `query_result` (after applying `result_subst`). + Ok(self.unify_canonical_vars(cause, param_env, original_values, substituted_query_result)?) } /// Converts the region constraints resulting from a query into an From 977f3fc9403624def9a2d030e5542f73bd26bb1f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Jun 2018 15:00:55 -0400 Subject: [PATCH 47/72] introduce `QueryKey` separation --- src/librustc/traits/query/type_op/eq.rs | 5 +++++ src/librustc/traits/query/type_op/mod.rs | 10 +++++++--- src/librustc/traits/query/type_op/normalize.rs | 5 +++++ src/librustc/traits/query/type_op/prove_predicate.rs | 5 +++++ src/librustc/traits/query/type_op/subtype.rs | 5 +++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index cc5f721518411..b70a5307097bd 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -26,6 +26,7 @@ impl<'tcx> Eq<'tcx> { } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { + type QueryKey = Self; type QueryResult = (); fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { @@ -36,6 +37,10 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { } } + fn into_query_key(self) -> Self { + self + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { self.param_env } diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 4addb6c027b95..41d33338db459 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -103,18 +103,21 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { } } -pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { +pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { + type QueryKey: TypeFoldable<'tcx> + Lift<'gcx>; type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; /// Micro-optimization: returns `Ok(x)` if we can trivially /// produce the output, else returns `Err(self)` back. fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; + fn into_query_key(self) -> Self::QueryKey; + fn param_env(&self) -> ParamEnv<'tcx>; fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Self>, + canonicalized: Canonicalized<'gcx, Self::QueryKey>, ) -> Fallible>; /// "Upcasts" a lifted query result (which is in the gcx lifetime) @@ -149,7 +152,8 @@ where // `canonicalize_hr_query_hack` here because of things like // the subtype query, which go awry around `'static` // otherwise. - let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&self); + let query_key = self.into_query_key(); + let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); let canonical_result = Q::perform_query(infcx.tcx, canonical_self)?; // FIXME: This is not the most efficient setup. The diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index 80bcc20f842c8..8c5bbe05616a0 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -33,6 +33,7 @@ impl<'gcx: 'tcx, 'tcx, T> super::QueryTypeOp<'gcx, 'tcx> for Normalize<'tcx, T> where T: Normalizable<'gcx, 'tcx>, { + type QueryKey = Self; type QueryResult = T; fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { @@ -43,6 +44,10 @@ where } } + fn into_query_key(self) -> Self { + self + } + fn param_env(&self) -> ParamEnv<'tcx> { self.param_env } diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index d2714803eaecc..1d02fe9a11317 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -28,12 +28,17 @@ impl<'tcx> ProvePredicate<'tcx> { } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { + type QueryKey = Self; type QueryResult = (); fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { Err(self) } + fn into_query_key(self) -> Self { + self + } + fn param_env(&self) -> ParamEnv<'tcx> { self.param_env } diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index 7bb1eda4eecfe..0842b78ce8b1c 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -30,6 +30,7 @@ impl<'tcx> Subtype<'tcx> { } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { + type QueryKey = Self; type QueryResult = (); fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self> { @@ -40,6 +41,10 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } + fn into_query_key(self) -> Self { + self + } + fn param_env(&self) -> ParamEnv<'tcx> { self.param_env } From e895f3aded36fd69f78232d4aca230a236948b8f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Jun 2018 17:25:32 -0400 Subject: [PATCH 48/72] convert `dropck_outlives` type-op to use the query --- src/librustc/traits/query/dropck_outlives.rs | 54 +++++++++++++------ src/librustc/traits/query/type_op/outlives.rs | 45 ++++++++++------ .../borrow_check/nll/type_check/liveness.rs | 16 ++++-- 3 files changed, 78 insertions(+), 37 deletions(-) diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index b843ae8f11afd..2aaa32aa03202 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -11,6 +11,7 @@ use infer::at::At; use infer::InferOk; use std::iter::FromIterator; +use syntax::codemap::Span; use ty::subst::Kind; use ty::{self, Ty, TyCtxt}; @@ -60,22 +61,9 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { &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(); - } - + Ok(InferOk { value, obligations }) => { + let ty = self.infcx.resolve_type_vars_if_possible(&ty); + let kinds = value.into_kinds_reporting_overflows(tcx, span, ty); return InferOk { value: kinds, obligations, @@ -102,12 +90,44 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, pub overflows: Vec>, } +impl<'tcx> DropckOutlivesResult<'tcx> { + pub fn report_overflows( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + span: Span, + ty: Ty<'tcx>, + ) { + for overflow_ty in self.overflows.iter().take(1) { + let mut err = struct_span_err!( + tcx.sess, + span, + E0320, + "overflow while adding drop-check rules for {}", + ty, + ); + err.note(&format!("overflowed on {}", overflow_ty)); + err.emit(); + } + } + + pub fn into_kinds_reporting_overflows( + self, + tcx: TyCtxt<'_, '_, 'tcx>, + span: Span, + ty: Ty<'tcx>, + ) -> Vec> { + 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. #[derive(Clone, Debug)] diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index 419146d82ec4d..a6fbb1650987c 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferOk}; +use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; use traits::query::dropck_outlives::trivial_dropck_outlives; +use traits::query::dropck_outlives::DropckOutlivesResult; use traits::query::Fallible; -use traits::ObligationCause; -use ty::subst::Kind; -use ty::{ParamEnv, Ty, TyCtxt}; +use ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; #[derive(Debug)] pub struct DropckOutlives<'tcx> { @@ -30,23 +29,39 @@ impl<'tcx> DropckOutlives<'tcx> { } } -impl<'gcx, 'tcx> super::TypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> { - type Output = Vec>; +impl super::QueryTypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> +where + 'gcx: 'tcx, +{ + type QueryKey = ParamEnvAnd<'tcx, Ty<'tcx>>; + type QueryResult = DropckOutlivesResult<'tcx>; - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if trivial_dropck_outlives(tcx, self.dropped_ty) { - Ok(vec![]) + Ok(DropckOutlivesResult::default()) } else { Err(self) } } - fn perform( - self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible>>> { - Ok(infcx - .at(&ObligationCause::dummy(), self.param_env) - .dropck_outlives(self.dropped_ty)) + fn param_env(&self) -> ParamEnv<'tcx> { + self.param_env + } + + fn into_query_key(self) -> Self::QueryKey { + self.param_env.and(self.dropped_ty) + } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, Self::QueryKey>, + ) -> Fallible> { + tcx.dropck_outlives(canonicalized) + } + + fn upcast_result( + lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>> { + lifted_query_result } } 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 b6cdbfa618187..73ed695bfb276 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -13,12 +13,12 @@ use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; +use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::infer::canonical::QueryRegionConstraint; use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::traits::query::type_op::outlives::DropckOutlives; use rustc::traits::query::type_op::TypeOp; -use rustc::ty::subst::Kind; use rustc::ty::{Ty, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; use std::rc::Rc; @@ -71,7 +71,7 @@ where } struct DropData<'tcx> { - dropped_kinds: Vec>, + dropck_result: DropckOutlivesResult<'tcx>, region_constraint_data: Option>>>, } @@ -202,10 +202,16 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo self.cx.push_region_constraints(location.at_self(), data); } + drop_data.dropck_result.report_overflows( + self.cx.infcx.tcx, + self.mir.source_info(location).span, + dropped_ty, + ); + // 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); - for &kind in &drop_data.dropped_kinds { + for &kind in &drop_data.dropck_result.kinds { Self::push_type_live_constraint(&mut self.cx, kind, location, cause); } } @@ -217,12 +223,12 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); let param_env = cx.param_env; - let (dropped_kinds, region_constraint_data) = DropckOutlives::new(param_env, dropped_ty) + let (dropck_result, region_constraint_data) = DropckOutlives::new(param_env, dropped_ty) .fully_perform(cx.infcx) .unwrap(); DropData { - dropped_kinds, + dropck_result, region_constraint_data, } } From a583269af57e6acce6d9c2ffe4c0ce2b4f2e1ab9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Jun 2018 10:01:08 -0400 Subject: [PATCH 49/72] add a streamlined `instantiate_query_result` method for NLL queries --- src/Cargo.lock | 1 + src/librustc/Cargo.toml | 1 + src/librustc/infer/canonical/query_result.rs | 86 +++++++++++++++++++- src/librustc/lib.rs | 1 + 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index b74587e566210..f8551dffcf4bf 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1781,6 +1781,7 @@ dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "chalk-engine 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "fmt_macros 0.0.0", "graphviz 0.0.0", diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 457a9f2f625ec..542ee9f02c909 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["dylib"] [dependencies] arena = { path = "../libarena" } bitflags = "1.0" +either = "1.5.0" fmt_macros = { path = "../libfmt_macros" } graphviz = { path = "../libgraphviz" } jobserver = "0.1" diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 71cefda6b6646..d512bb5268da3 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -17,10 +17,11 @@ //! //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html +use either::Either; use infer::canonical::substitute::substitute_value; use infer::canonical::{ - Canonical, CanonicalVarValues, CanonicalizedQueryResult, Certainty, QueryRegionConstraint, - QueryResult, + Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, Certainty, + QueryRegionConstraint, QueryResult, }; use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; @@ -28,6 +29,7 @@ use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::fmt::Debug; +use std::iter::once; use syntax::ast; use traits::query::NoSolution; use traits::{FulfillmentContext, TraitEngine}; @@ -176,6 +178,86 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { }) } + /// NLL does a lot of queries that have a particular form that we + /// can take advantage of to be more efficient. These queries do + /// not have any *type* inference variables, only region inference + /// variables. Therefore, when we instantiate the query result, we + /// only ever produce new *region constraints* and never other + /// forms of obligations (moreover, since we only determine + /// satisfiability modulo region constraints, instantiation is + /// infallible). Therefore, the return value need only be a larger + /// set of query region constraints. These constraints can then be + /// added directly to the NLL inference context. + pub fn instantiate_nll_query_result_and_region_obligations( + &self, + cause: &ObligationCause<'tcx>, + original_values: &CanonicalVarValues<'tcx>, + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, + ) -> Vec> + where + R: Debug + TypeFoldable<'tcx>, + { + // In an NLL query, there should be no type variables in the + // query, only region variables. + debug_assert!(query_result.variables.iter().all(|v| match v.kind { + CanonicalVarKind::Ty(_) => false, + CanonicalVarKind::Region => true, + })); + + let result_subst = + self.query_result_substitution_guess(cause, original_values, query_result); + + // Compute `QueryRegionConstraint` values that unify each of + // the original values `v_o` that was canonicalized into a + // variable... + let qrc_from_unify = original_values.var_values.iter_enumerated().flat_map( + |(index, original_value)| { + // ...with the value `v_r` of that variable from the query. + let result_value = + query_result + .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); + match (original_value.unpack(), result_value.unpack()) { + ( + UnpackedKind::Lifetime(ty::ReErased), + UnpackedKind::Lifetime(ty::ReErased), + ) => { + // no action needed + Either::Left(None.into_iter()) + } + + (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { + // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. + Either::Right( + once(ty::OutlivesPredicate(v_o.into(), v_r)) + .chain(once(ty::OutlivesPredicate(v_r.into(), v_o))) + .map(ty::Binder::dummy), + ) + } + + (UnpackedKind::Type(_), _) | (_, UnpackedKind::Type(_)) => { + // in NLL queries, we do not expect `type` results. + bug!( + "unexpected type in NLL query: cannot unify {:?} and {:?}", + original_value, + result_value, + ); + } + } + }, + ); + + // ...also include the other query region constraints from the query. + let qrc_from_result = query_result.value.region_constraints.iter().map(|r_c| { + r_c.map_bound(|ty::OutlivesPredicate(k1, r2)| { + let k1 = substitute_value(self.tcx, &result_subst, &k1); + let r2 = substitute_value(self.tcx, &result_subst, &r2); + ty::OutlivesPredicate(k1, r2) + }) + }); + + qrc_from_unify.chain(qrc_from_result).collect() + } + /// Given the original values and the (canonicalized) result from /// computing a query, returns a substitution that can be applied /// to the query result to convert the result back into the diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 102efe2bef3f7..7c3d3414846de 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -79,6 +79,7 @@ extern crate arena; #[macro_use] extern crate bitflags; extern crate core; +extern crate either; extern crate fmt_macros; extern crate getopts; extern crate graphviz; From 82169b6134b34f17b0e7c247dbd92bb6949951c0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Jun 2018 18:32:43 -0400 Subject: [PATCH 50/72] convert query-type-op to create query-region-constraint directly --- src/librustc/infer/canonical/query_result.rs | 85 +++++---- src/librustc/traits/query/type_op/custom.rs | 57 +++++- src/librustc/traits/query/type_op/eq.rs | 12 +- src/librustc/traits/query/type_op/mod.rs | 170 +++++++----------- .../traits/query/type_op/normalize.rs | 10 +- src/librustc/traits/query/type_op/outlives.rs | 12 +- .../traits/query/type_op/prove_predicate.rs | 10 +- src/librustc/traits/query/type_op/subtype.rs | 10 +- 8 files changed, 179 insertions(+), 187 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index d512bb5268da3..8a3784fe08822 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -17,7 +17,6 @@ //! //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html -use either::Either; use infer::canonical::substitute::substitute_value; use infer::canonical::{ Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, Certainty, @@ -29,7 +28,6 @@ use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::fmt::Debug; -use std::iter::once; use syntax::ast; use traits::query::NoSolution; use traits::{FulfillmentContext, TraitEngine}; @@ -191,9 +189,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { pub fn instantiate_nll_query_result_and_region_obligations( &self, cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, original_values: &CanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, - ) -> Vec> + output_query_region_constraints: &mut Vec>, + ) -> InferResult<'tcx, R> where R: Debug + TypeFoldable<'tcx>, { @@ -210,52 +210,59 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // Compute `QueryRegionConstraint` values that unify each of // the original values `v_o` that was canonicalized into a // variable... - let qrc_from_unify = original_values.var_values.iter_enumerated().flat_map( - |(index, original_value)| { - // ...with the value `v_r` of that variable from the query. - let result_value = - query_result - .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); - match (original_value.unpack(), result_value.unpack()) { - ( - UnpackedKind::Lifetime(ty::ReErased), - UnpackedKind::Lifetime(ty::ReErased), - ) => { - // no action needed - Either::Left(None.into_iter()) - } + let mut obligations = vec![]; + + for (index, original_value) in original_values.var_values.iter_enumerated() { + // ...with the value `v_r` of that variable from the query. + let result_value = query_result + .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); + match (original_value.unpack(), result_value.unpack()) { + (UnpackedKind::Lifetime(ty::ReErased), UnpackedKind::Lifetime(ty::ReErased)) => { + // no action needed + } - (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { - // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. - Either::Right( - once(ty::OutlivesPredicate(v_o.into(), v_r)) - .chain(once(ty::OutlivesPredicate(v_r.into(), v_o))) - .map(ty::Binder::dummy), - ) + (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { + // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. + if v_o != v_r { + output_query_region_constraints + .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); + output_query_region_constraints + .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); } + } - (UnpackedKind::Type(_), _) | (_, UnpackedKind::Type(_)) => { - // in NLL queries, we do not expect `type` results. - bug!( - "unexpected type in NLL query: cannot unify {:?} and {:?}", - original_value, - result_value, - ); - } + (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { + let ok = self.at(cause, param_env).eq(v1, v2)?; + obligations.extend(ok.into_obligations()); } - }, - ); + + _ => { + bug!( + "kind mismatch, cannot unify {:?} and {:?}", + original_value, + result_value + ); + } + } + } // ...also include the other query region constraints from the query. - let qrc_from_result = query_result.value.region_constraints.iter().map(|r_c| { - r_c.map_bound(|ty::OutlivesPredicate(k1, r2)| { + output_query_region_constraints.reserve(query_result.value.region_constraints.len()); + for r_c in query_result.value.region_constraints.iter() { + output_query_region_constraints.push(r_c.map_bound(|ty::OutlivesPredicate(k1, r2)| { let k1 = substitute_value(self.tcx, &result_subst, &k1); let r2 = substitute_value(self.tcx, &result_subst, &r2); ty::OutlivesPredicate(k1, r2) - }) - }); + })); + } - qrc_from_unify.chain(qrc_from_result).collect() + let user_result: R = + query_result.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value); + + Ok(InferOk { + value: user_result, + obligations, + }) } /// Given the original values and the (canonicalized) result from diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index d3bc81a09a995..90ea276a09449 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -9,9 +9,14 @@ // except according to those terms. use infer::{InferCtxt, InferOk}; -use traits::query::Fallible; -use ty::TyCtxt; use std::fmt; +use traits::query::Fallible; + +use infer::canonical::query_result; +use infer::canonical::QueryRegionConstraint; +use std::rc::Rc; +use syntax::codemap::DUMMY_SP; +use traits::{ObligationCause, TraitEngine}; pub struct CustomTypeOp { closure: F, @@ -38,12 +43,18 @@ where { type Output = R; - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - Err(self) - } + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ) -> Fallible<(Self::Output, Option>>>)> { + if cfg!(debug_assertions) { + info!("fully_perform({:?})", self); + } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> Fallible> { - Ok((self.closure)(infcx)?) + scrape_region_constraints(infcx, || Ok((self.closure)(infcx)?)) } } @@ -55,3 +66,35 @@ where write!(f, "{}", (self.description)()) } } + +/// Executes `op` and then scrapes out all the "old style" region +/// constraints that result, creating query-region-constraints. +fn scrape_region_constraints<'gcx, 'tcx, R>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + op: impl FnOnce() -> Fallible>, +) -> Fallible<(R, Option>>>)> { + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let dummy_body_id = ObligationCause::dummy().body_id; + let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; + debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); + fulfill_cx.register_predicate_obligations(infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(infcx) { + infcx.tcx.sess.diagnostic().delay_span_bug( + DUMMY_SP, + &format!("errors selecting obligation during MIR typeck: {:?}", e), + ); + } + + let region_obligations = infcx.take_registered_region_obligations(); + + let region_constraint_data = infcx.take_and_reset_region_constraints(); + + let outlives = + query_result::make_query_outlives(infcx.tcx, region_obligations, ®ion_constraint_data); + + if outlives.is_empty() { + Ok((value, None)) + } else { + Ok((value, Some(Rc::new(outlives)))) + } +} diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index b70a5307097bd..04d256a93e12a 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -10,7 +10,7 @@ use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; use traits::query::Fallible; -use ty::{self, ParamEnv, Ty, TyCtxt}; +use ty::{ParamEnv, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Eq<'tcx> { @@ -29,7 +29,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { type QueryKey = Self; type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if self.a == self.b { Ok(()) } else { @@ -37,12 +37,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { } } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 41d33338db459..16280885c12ad 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -8,16 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::query_result; use infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, QueryResult, }; use infer::{InferCtxt, InferOk}; use std::fmt; use std::rc::Rc; -use syntax::codemap::DUMMY_SP; use traits::query::Fallible; -use traits::{ObligationCause, TraitEngine}; +use traits::ObligationCause; use ty::fold::TypeFoldable; use ty::{Lift, ParamEnv, TyCtxt}; @@ -31,89 +29,25 @@ pub mod subtype; pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; - /// Micro-optimization: returns `Ok(x)` if we can trivially - /// produce the output, else returns `Err(self)` back. - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; - - /// Given an infcx, performs **the kernel** of the operation: this does the - /// key action and then, optionally, returns a set of obligations which must be proven. - /// - /// This method is not meant to be invoked directly: instead, one - /// should use `fully_perform`, which will take those resulting - /// obligations and prove them, and then process the combined - /// results into region obligations which are returned. - fn perform( - self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible>; - /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { - match self.trivial_noop(infcx.tcx) { - Ok(r) => Ok((r, None)), - Err(op) => op.fully_perform_nontrivial(infcx), - } - } - - /// Helper for `fully_perform` that handles the nontrivial cases. - #[inline(never)] // just to help with profiling - fn fully_perform_nontrivial( - self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { - if cfg!(debug_assertions) { - info!( - "fully_perform_op_and_get_region_constraint_data({:?})", - self - ); - } - - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - let dummy_body_id = ObligationCause::dummy().body_id; - let InferOk { value, obligations } = infcx.commit_if_ok(|_| self.perform(infcx))?; - debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); - fulfill_cx.register_predicate_obligations(infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(infcx) { - infcx.tcx.sess.diagnostic().delay_span_bug( - DUMMY_SP, - &format!("errors selecting obligation during MIR typeck: {:?}", e), - ); - } - - let region_obligations = infcx.take_registered_region_obligations(); - - let region_constraint_data = infcx.take_and_reset_region_constraints(); - - let outlives = query_result::make_query_outlives( - infcx.tcx, - region_obligations, - ®ion_constraint_data, - ); - - if outlives.is_empty() { - Ok((value, None)) - } else { - Ok((value, Some(Rc::new(outlives)))) - } - } + ) -> Fallible<(Self::Output, Option>>>)>; } pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { type QueryKey: TypeFoldable<'tcx> + Lift<'gcx>; type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; - /// Micro-optimization: returns `Ok(x)` if we can trivially - /// produce the output, else returns `Err(self)` back. - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; - - fn into_query_key(self) -> Self::QueryKey; + /// Either converts `self` directly into a `QueryResult` (for + /// simple cases) or into a `QueryKey` (for more complex cases + /// where we actually have work to do). + fn prequery(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; - fn param_env(&self) -> ParamEnv<'tcx>; + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx>; fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, @@ -130,6 +64,51 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { fn upcast_result( lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>; + + fn fully_perform_into( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + output_query_region_constraints: &mut Vec>, + ) -> Fallible { + match QueryTypeOp::prequery(self, infcx.tcx) { + Ok(result) => Ok(result), + Err(query_key) => { + // FIXME(#33684) -- We need to use + // `canonicalize_hr_query_hack` here because of things + // like the subtype query, which go awry around + // `'static` otherwise. + let (canonical_self, canonical_var_values) = + infcx.canonicalize_hr_query_hack(&query_key); + let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; + let canonical_result = Self::upcast_result(&canonical_result); + + let param_env = Self::param_env(&query_key); + + let InferOk { value, obligations } = infcx + .instantiate_nll_query_result_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + output_query_region_constraints, + )?; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + for obligation in obligations { + let () = prove_predicate::ProvePredicate::new( + obligation.param_env, + obligation.predicate, + ).fully_perform_into(infcx, output_query_region_constraints)?; + } + + Ok(value) + } + } + } } impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for Q @@ -138,38 +117,21 @@ where { type Output = Q::QueryResult; - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - QueryTypeOp::trivial_noop(self, tcx) - } - - fn perform( + fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible> { - let param_env = self.param_env(); - - // FIXME(#33684) -- We need to use - // `canonicalize_hr_query_hack` here because of things like - // the subtype query, which go awry around `'static` - // otherwise. - let query_key = self.into_query_key(); - let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); - let canonical_result = Q::perform_query(infcx.tcx, canonical_self)?; - - // FIXME: This is not the most efficient setup. The - // `instantiate_query_result_and_region_obligations` basically - // takes the `QueryRegionConstraint` values that we ultimately - // want to use and converts them into obligations. We return - // those to our caller, which will convert them into AST - // region constraints; we then convert *those* back into - // `QueryRegionConstraint` and ultimately into NLL - // constraints. We should cut out the middleman but that will - // take a bit of refactoring. - Ok(infcx.instantiate_query_result_and_region_obligations( - &ObligationCause::dummy(), - param_env, - &canonical_var_values, - Q::upcast_result(&canonical_result), - )?) + ) -> Fallible<(Self::Output, Option>>>)> { + let mut qrc = vec![]; + let r = Q::fully_perform_into(self, infcx, &mut qrc)?; + + // Promote the final query-region-constraints into a + // (optional) ref-counted vector: + let opt_qrc = if qrc.is_empty() { + None + } else { + Some(Rc::new(qrc)) + }; + + Ok((r, opt_qrc)) } } diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index 8c5bbe05616a0..b72c887ba50c1 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -36,7 +36,7 @@ where type QueryKey = Self; type QueryResult = T; - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if !self.value.has_projections() { Ok(self.value) } else { @@ -44,12 +44,8 @@ where } } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index a6fbb1650987c..0254b901a8c58 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -36,20 +36,16 @@ where type QueryKey = ParamEnvAnd<'tcx, Ty<'tcx>>; type QueryResult = DropckOutlivesResult<'tcx>; - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn prequery(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { if trivial_dropck_outlives(tcx, self.dropped_ty) { Ok(DropckOutlivesResult::default()) } else { - Err(self) + Err(self.param_env.and(self.dropped_ty)) } } - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env - } - - fn into_query_key(self) -> Self::QueryKey { - self.param_env.and(self.dropped_ty) + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 1d02fe9a11317..b06ad32ff2232 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -31,16 +31,12 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { type QueryKey = Self; type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { Err(self) } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index 0842b78ce8b1c..bdc7dd9589b67 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -33,7 +33,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { type QueryKey = Self; type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self> { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self::QueryKey> { if self.sub == self.sup { Ok(()) } else { @@ -41,12 +41,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( From e98d376e6f15781223717a8dfca8df8b47c9ab2a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 26 Jun 2018 07:12:51 -0400 Subject: [PATCH 51/72] do not re-create the `LocationTable` for every path :facepalm: --- src/librustc_mir/borrow_check/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 6d77364aae02a..cd58dd11ac66c 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -245,6 +245,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( mir_def_id: def_id, move_data: &mdpe.move_data, param_env: param_env, + location_table, movable_generator, locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false, @@ -332,6 +333,11 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { mir: &'cx Mir<'tcx>, mir_def_id: DefId, move_data: &'cx MoveData<'tcx>, + + /// Map from MIR `Location` to `LocationIndex`; created + /// when MIR borrowck begins. + location_table: &'cx LocationTable, + param_env: ParamEnv<'gcx>, movable_generator: bool, /// This keeps track of whether local variables are free-ed when the function @@ -946,8 +952,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let mut error_reported = false; let tcx = self.tcx; let mir = self.mir; - let location_table = &LocationTable::new(mir); - let location = location_table.start_index(context.loc); + let location = self.location_table.start_index(context.loc); let borrow_set = self.borrow_set.clone(); each_borrow_involving_path( self, From 188ad384a33c35cf6e2328a0424c59928ea1b64c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:01:19 -0400 Subject: [PATCH 52/72] nit: don't import Debug directly --- src/librustc/ty/context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 111167cfc1090..5142a30ae574f 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -64,7 +64,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::hash_map::{self, Entry}; use std::hash::{Hash, Hasher}; -use std::fmt::Debug; +use std::fmt; use std::mem; use std::ops::Deref; use std::iter; @@ -1504,8 +1504,8 @@ impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> { /// contain the TypeVariants key or if the address of the interned /// pointer differs. The latter case is possible if a primitive type, /// e.g. `()` or `u8`, was interned in a different context. -pub trait Lift<'tcx>: Debug { - type Lifted: Debug + 'tcx; +pub trait Lift<'tcx>: fmt::Debug { + type Lifted: fmt::Debug + 'tcx; fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option; } From 2fd8a312d9883b322981be03bd1c17308354634d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:48:32 -0400 Subject: [PATCH 53/72] extract out query boilerplate and use for `Eq` --- src/librustc/infer/canonical/query_result.rs | 57 +++++++++++++++----- src/librustc_traits/type_op_eq.rs | 19 ++----- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 8a3784fe08822..9615ee2d3b198 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -18,24 +18,54 @@ //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html use infer::canonical::substitute::substitute_value; -use infer::canonical::{ - Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, Certainty, - QueryRegionConstraint, QueryResult, -}; +use infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, + Certainty, QueryRegionConstraint, QueryResult}; use infer::region_constraints::{Constraint, RegionConstraintData}; +use infer::InferCtxtBuilder; use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::fmt::Debug; use syntax::ast; -use traits::query::NoSolution; +use syntax_pos::DUMMY_SP; +use traits::query::{Fallible, NoSolution}; use traits::{FulfillmentContext, TraitEngine}; use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::fold::TypeFoldable; use ty::subst::{Kind, UnpackedKind}; use ty::{self, CanonicalVar, Lift, TyCtxt}; +impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { + /// The "main method" for a canonicalized trait query. Given the + /// canonical key `canonical_key`, this method will create a new + /// inference context, instantiate the key, and run your operation + /// `op`. The operation should yield up a result (of type `R`) as + /// well as a set of trait obligations that must be fully + /// satisfied. These obligations will be processed and the + /// canonical result created. + /// + /// Returns `NoSolution` in the event of any error. + pub fn enter_canonical_trait_query( + &'tcx mut self, + canonical_key: &Canonical<'tcx, K>, + op: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, K) -> Fallible>, + ) -> Fallible> + where + K: TypeFoldable<'tcx>, + R: Debug + Lift<'gcx> + TypeFoldable<'tcx>, + { + self.enter(|ref infcx| { + let (key, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_key); + let InferOk { value, obligations } = op(infcx, key)?; + let fulfill_cx = &mut FulfillmentContext::new(); + fulfill_cx.register_predicate_obligations(infcx, obligations); + infcx.make_canonicalized_query_result(canonical_inference_vars, value, fulfill_cx) + }) + } +} + impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// This method is meant to be invoked as the final step of a canonical query /// implementation. It is given: @@ -61,7 +91,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { inference_vars: CanonicalVarValues<'tcx>, answer: T, fulfill_cx: &mut FulfillmentContext<'tcx>, - ) -> Result, NoSolution> + ) -> Fallible> where T: Debug + Lift<'gcx> + TypeFoldable<'tcx>, { @@ -293,14 +323,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { let result_subst = self.query_result_substitution_guess(cause, original_values, query_result); - let obligations = self - .unify_query_result_substitution_guess( - cause, - param_env, - original_values, - &result_subst, - query_result, - )? + let obligations = self.unify_query_result_substitution_guess( + cause, + param_env, + original_values, + &result_subst, + query_result, + )? .into_obligations(); Ok(InferOk { diff --git a/src/librustc_traits/type_op_eq.rs b/src/librustc_traits/type_op_eq.rs index b73bee4648657..511203bf2fc9d 100644 --- a/src/librustc_traits/type_op_eq.rs +++ b/src/librustc_traits/type_op_eq.rs @@ -11,25 +11,16 @@ use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::traits::query::type_op::eq::Eq; use rustc::traits::query::NoSolution; -use rustc::traits::{FulfillmentContext, ObligationCause}; +use rustc::traits::ObligationCause; use rustc::ty::TyCtxt; use rustc_data_structures::sync::Lrc; -use syntax::codemap::DUMMY_SP; crate fn type_op_eq<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Eq<'tcx>>, ) -> Result>>, NoSolution> { - let tcx = tcx.global_tcx(); - tcx.infer_ctxt().enter(|ref infcx| { - let (Eq { param_env, a, b }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); - let fulfill_cx = &mut FulfillmentContext::new(); - let obligations = match infcx.at(&ObligationCause::dummy(), param_env).eq(a, b) { - Ok(v) => v.into_obligations(), - Err(_) => return Err(NoSolution), - }; - fulfill_cx.register_predicate_obligations(infcx, obligations); - infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) - }) + tcx.infer_ctxt() + .enter_canonical_trait_query(&canonicalized, |infcx, Eq { param_env, a, b }| { + Ok(infcx.at(&ObligationCause::dummy(), param_env).eq(a, b)?) + }) } From fa71af419207fbd0169f1e2d36abcfaf3bc4fab2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:48:43 -0400 Subject: [PATCH 54/72] use query boilerplate for `normalize` --- src/librustc_traits/type_op_normalize.rs | 36 +++++++++++------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/librustc_traits/type_op_normalize.rs b/src/librustc_traits/type_op_normalize.rs index edfe627b15a3a..78e8a0ff3e698 100644 --- a/src/librustc_traits/type_op_normalize.rs +++ b/src/librustc_traits/type_op_normalize.rs @@ -9,60 +9,56 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::infer::InferCtxt; +use rustc::infer::{InferCtxt, InferOk}; use rustc::traits::query::type_op::normalize::Normalize; -use rustc::traits::query::NoSolution; -use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; +use rustc::traits::query::{Fallible, NoSolution}; +use rustc::traits::{Normalized, ObligationCause}; use rustc::ty::{FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::Lrc; use std::fmt; -use syntax::codemap::DUMMY_SP; -fn type_op_normalize<'gcx, 'tcx, T>( +fn type_op_normalize( infcx: &InferCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonical<'tcx, Normalize<'tcx, T>>, -) -> Result>::Lifted>>>, NoSolution> + key: Normalize<'tcx, T>, +) -> Fallible> where T: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx>, { - let (Normalize { param_env, value }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); - let fulfill_cx = &mut FulfillmentContext::new(); + let Normalize { param_env, value } = key; let Normalized { value, obligations } = infcx .at(&ObligationCause::dummy(), param_env) .normalize(&value)?; - fulfill_cx.register_predicate_obligations(infcx, obligations); - infcx.make_canonicalized_query_result(canonical_inference_vars, value, fulfill_cx) + Ok(InferOk { value, obligations }) // ugh we should merge these two structs } -crate fn type_op_normalize_ty<'tcx>( +crate fn type_op_normalize_ty( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, Ty<'tcx>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() - .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) + .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_normalize_predicate<'tcx>( +crate fn type_op_normalize_predicate( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, Predicate<'tcx>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() - .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) + .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_normalize_fn_sig<'tcx>( +crate fn type_op_normalize_fn_sig( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, FnSig<'tcx>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() - .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) + .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_normalize_poly_fn_sig<'tcx>( +crate fn type_op_normalize_poly_fn_sig( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, PolyFnSig<'tcx>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() - .enter(|ref infcx| type_op_normalize(infcx, canonicalized)) + .enter_canonical_trait_query(&canonicalized, type_op_normalize) } From ac40d73c6ff360fa260f842bf66c848c0ec81eec Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:48:52 -0400 Subject: [PATCH 55/72] use query boilerplate for subtype --- src/librustc_traits/type_op_subtype.rs | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustc_traits/type_op_subtype.rs b/src/librustc_traits/type_op_subtype.rs index 1fa5ec915f23b..30a8550f107b2 100644 --- a/src/librustc_traits/type_op_subtype.rs +++ b/src/librustc_traits/type_op_subtype.rs @@ -11,25 +11,25 @@ use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::traits::query::type_op::subtype::Subtype; use rustc::traits::query::NoSolution; -use rustc::traits::{FulfillmentContext, ObligationCause}; +use rustc::traits::ObligationCause; use rustc::ty::TyCtxt; use rustc_data_structures::sync::Lrc; -use syntax::codemap::DUMMY_SP; crate fn type_op_subtype<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Subtype<'tcx>>, ) -> Result>>, NoSolution> { - let tcx = tcx.global_tcx(); - tcx.infer_ctxt().enter(|ref infcx| { - let (Subtype { param_env, sub, sup }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); - let fulfill_cx = &mut FulfillmentContext::new(); - let obligations = match infcx.at(&ObligationCause::dummy(), param_env).sup(sup, sub) { - Ok(v) => v.into_obligations(), - Err(_) => return Err(NoSolution), - }; - fulfill_cx.register_predicate_obligations(infcx, obligations); - infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) - }) + tcx.infer_ctxt().enter_canonical_trait_query( + &canonicalized, + |infcx, + Subtype { + param_env, + sub, + sup, + }| { + Ok(infcx + .at(&ObligationCause::dummy(), param_env) + .sup(sup, sub)?) + }, + ) } From e6c8c632b73962184e5cc33106e54de95dcec8ae Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:49:05 -0400 Subject: [PATCH 56/72] use query boilerplate for prove-predicate -- slightly inefficient This requires us to allocate a single entry vector we didn't use to allocate. I doubt this makes a difference in practice, as this only occurs for cache misses. --- .../type_op_prove_predicate.rs | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/librustc_traits/type_op_prove_predicate.rs b/src/librustc_traits/type_op_prove_predicate.rs index ad16e6d31d7a0..7210d43d9f75e 100644 --- a/src/librustc_traits/type_op_prove_predicate.rs +++ b/src/librustc_traits/type_op_prove_predicate.rs @@ -8,25 +8,31 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::infer::InferOk; use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::traits::query::type_op::prove_predicate::ProvePredicate; use rustc::traits::query::NoSolution; -use rustc::traits::{FulfillmentContext, Obligation, ObligationCause, TraitEngine}; +use rustc::traits::{Obligation, ObligationCause}; use rustc::ty::TyCtxt; use rustc_data_structures::sync::Lrc; -use syntax::codemap::DUMMY_SP; crate fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, ProvePredicate<'tcx>>, ) -> Result>>, NoSolution> { - let tcx = tcx.global_tcx(); - tcx.infer_ctxt().enter(|ref infcx| { - let (ProvePredicate { param_env, predicate }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonicalized); - let fulfill_cx = &mut FulfillmentContext::new(); - let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); - fulfill_cx.register_predicate_obligation(infcx, obligation); - infcx.make_canonicalized_query_result(canonical_inference_vars, (), fulfill_cx) - }) + tcx.infer_ctxt() + .enter_canonical_trait_query(&canonicalized, |_infcx, key| { + let ProvePredicate { + param_env, + predicate, + } = key; + Ok(InferOk { + value: (), + obligations: vec![Obligation::new( + ObligationCause::dummy(), + param_env, + predicate, + )], + }) + }) } From 66c88392b4594507b8b0282d8a3de5f43922acc4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:47:55 -0400 Subject: [PATCH 57/72] use query boiler plate for `normalize_projection_ty` too --- .../normalize_projection_ty.rs | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index a9c4fef9f7dc1..473b2c8e99850 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -9,13 +9,14 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext}; -use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult}; +use rustc::infer::InferOk; +use rustc::traits::query::{normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution}; +use rustc::traits::{self, ObligationCause, SelectionContext}; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; +use std::sync::atomic::Ordering; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::DUMMY_SP; -use std::sync::atomic::Ordering; crate fn normalize_projection_ty<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, @@ -23,29 +24,34 @@ crate fn normalize_projection_ty<'tcx>( ) -> Result>>>, NoSolution> { debug!("normalize_provider(goal={:#?})", goal); - tcx.sess.perf_stats.normalize_projection_ty.fetch_add(1, Ordering::Relaxed); - tcx.infer_ctxt().enter(|ref infcx| { - let ( - ParamEnvAnd { + tcx.sess + .perf_stats + .normalize_projection_ty + .fetch_add(1, Ordering::Relaxed); + tcx.infer_ctxt().enter_canonical_trait_query( + &goal, + |infcx, + ParamEnvAnd { + param_env, + value: goal, + }| { + let selcx = &mut SelectionContext::new(infcx); + let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); + let mut obligations = vec![]; + let answer = traits::normalize_projection_type( + selcx, param_env, - value: goal, - }, - canonical_inference_vars, - ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal); - let fulfill_cx = &mut FulfillmentContext::new(); - let selcx = &mut SelectionContext::new(infcx); - let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); - let mut obligations = vec![]; - let answer = - traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations); - fulfill_cx.register_predicate_obligations(infcx, obligations); - - // Now that we have fulfilled as much as we can, create a solution - // from what we've learned. - infcx.make_canonicalized_query_result( - canonical_inference_vars, - NormalizationResult { normalized_ty: answer }, - fulfill_cx, - ) - }) + goal, + cause, + 0, + &mut obligations, + ); + Ok(InferOk { + value: NormalizationResult { + normalized_ty: answer, + }, + obligations, + }) + }, + ) } From d49d5222a9212cdcda35498383d651872d34a825 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:53:23 -0400 Subject: [PATCH 58/72] merge all the `type_op_foo` modules into one as they are so trivial --- src/librustc_traits/lib.rs | 19 +++---- .../{type_op_normalize.rs => type_op.rs} | 55 ++++++++++++++++++- src/librustc_traits/type_op_eq.rs | 26 --------- .../type_op_prove_predicate.rs | 38 ------------- src/librustc_traits/type_op_subtype.rs | 35 ------------ 5 files changed, 62 insertions(+), 111 deletions(-) rename src/librustc_traits/{type_op_normalize.rs => type_op.rs} (58%) delete mode 100644 src/librustc_traits/type_op_eq.rs delete mode 100644 src/librustc_traits/type_op_prove_predicate.rs delete mode 100644 src/librustc_traits/type_op_subtype.rs diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index f81b6a2d906f9..b02d7f6c3f7b6 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -34,10 +34,7 @@ mod evaluate_obligation; mod normalize_projection_ty; mod normalize_erasing_regions; pub mod lowering; -mod type_op_eq; -mod type_op_normalize; -mod type_op_prove_predicate; -mod type_op_subtype; +mod type_op; use rustc::ty::query::Providers; @@ -51,13 +48,13 @@ pub fn provide(p: &mut Providers) { program_clauses_for: lowering::program_clauses_for, program_clauses_for_env: lowering::program_clauses_for_env, evaluate_obligation: evaluate_obligation::evaluate_obligation, - type_op_eq: type_op_eq::type_op_eq, - type_op_prove_predicate: type_op_prove_predicate::type_op_prove_predicate, - type_op_subtype: type_op_subtype::type_op_subtype, - type_op_normalize_ty: type_op_normalize::type_op_normalize_ty, - type_op_normalize_predicate: type_op_normalize::type_op_normalize_predicate, - type_op_normalize_fn_sig: type_op_normalize::type_op_normalize_fn_sig, - type_op_normalize_poly_fn_sig: type_op_normalize::type_op_normalize_poly_fn_sig, + type_op_eq: type_op::type_op_eq, + type_op_prove_predicate: type_op::type_op_prove_predicate, + type_op_subtype: type_op::type_op_subtype, + type_op_normalize_ty: type_op::type_op_normalize_ty, + type_op_normalize_predicate: type_op::type_op_normalize_predicate, + type_op_normalize_fn_sig: type_op::type_op_normalize_fn_sig, + type_op_normalize_poly_fn_sig: type_op::type_op_normalize_poly_fn_sig, ..*p }; } diff --git a/src/librustc_traits/type_op_normalize.rs b/src/librustc_traits/type_op.rs similarity index 58% rename from src/librustc_traits/type_op_normalize.rs rename to src/librustc_traits/type_op.rs index 78e8a0ff3e698..f44cd686b40a3 100644 --- a/src/librustc_traits/type_op_normalize.rs +++ b/src/librustc_traits/type_op.rs @@ -10,13 +10,26 @@ use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::infer::{InferCtxt, InferOk}; +use rustc::traits::query::type_op::eq::Eq; use rustc::traits::query::type_op::normalize::Normalize; +use rustc::traits::query::type_op::prove_predicate::ProvePredicate; +use rustc::traits::query::type_op::subtype::Subtype; use rustc::traits::query::{Fallible, NoSolution}; -use rustc::traits::{Normalized, ObligationCause}; +use rustc::traits::{Obligation, Normalized, ObligationCause}; use rustc::ty::{FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::Lrc; use std::fmt; +crate fn type_op_eq<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Eq<'tcx>>, +) -> Result>>, NoSolution> { + tcx.infer_ctxt() + .enter_canonical_trait_query(&canonicalized, |infcx, Eq { param_env, a, b }| { + Ok(infcx.at(&ObligationCause::dummy(), param_env).eq(a, b)?) + }) +} + fn type_op_normalize( infcx: &InferCtxt<'_, 'gcx, 'tcx>, key: Normalize<'tcx, T>, @@ -62,3 +75,43 @@ crate fn type_op_normalize_poly_fn_sig( tcx.infer_ctxt() .enter_canonical_trait_query(&canonicalized, type_op_normalize) } + +crate fn type_op_subtype<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, Subtype<'tcx>>, +) -> Result>>, NoSolution> { + tcx.infer_ctxt().enter_canonical_trait_query( + &canonicalized, + |infcx, + Subtype { + param_env, + sub, + sup, + }| { + Ok(infcx + .at(&ObligationCause::dummy(), param_env) + .sup(sup, sub)?) + }, + ) +} + +crate fn type_op_prove_predicate<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, ProvePredicate<'tcx>>, +) -> Result>>, NoSolution> { + tcx.infer_ctxt() + .enter_canonical_trait_query(&canonicalized, |_infcx, key| { + let ProvePredicate { + param_env, + predicate, + } = key; + Ok(InferOk { + value: (), + obligations: vec![Obligation::new( + ObligationCause::dummy(), + param_env, + predicate, + )], + }) + }) +} diff --git a/src/librustc_traits/type_op_eq.rs b/src/librustc_traits/type_op_eq.rs deleted file mode 100644 index 511203bf2fc9d..0000000000000 --- a/src/librustc_traits/type_op_eq.rs +++ /dev/null @@ -1,26 +0,0 @@ -// 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::traits::query::type_op::eq::Eq; -use rustc::traits::query::NoSolution; -use rustc::traits::ObligationCause; -use rustc::ty::TyCtxt; -use rustc_data_structures::sync::Lrc; - -crate fn type_op_eq<'tcx>( - tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Eq<'tcx>>, -) -> Result>>, NoSolution> { - tcx.infer_ctxt() - .enter_canonical_trait_query(&canonicalized, |infcx, Eq { param_env, a, b }| { - Ok(infcx.at(&ObligationCause::dummy(), param_env).eq(a, b)?) - }) -} diff --git a/src/librustc_traits/type_op_prove_predicate.rs b/src/librustc_traits/type_op_prove_predicate.rs deleted file mode 100644 index 7210d43d9f75e..0000000000000 --- a/src/librustc_traits/type_op_prove_predicate.rs +++ /dev/null @@ -1,38 +0,0 @@ -// 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::InferOk; -use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::traits::query::type_op::prove_predicate::ProvePredicate; -use rustc::traits::query::NoSolution; -use rustc::traits::{Obligation, ObligationCause}; -use rustc::ty::TyCtxt; -use rustc_data_structures::sync::Lrc; - -crate fn type_op_prove_predicate<'tcx>( - tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, ProvePredicate<'tcx>>, -) -> Result>>, NoSolution> { - tcx.infer_ctxt() - .enter_canonical_trait_query(&canonicalized, |_infcx, key| { - let ProvePredicate { - param_env, - predicate, - } = key; - Ok(InferOk { - value: (), - obligations: vec![Obligation::new( - ObligationCause::dummy(), - param_env, - predicate, - )], - }) - }) -} diff --git a/src/librustc_traits/type_op_subtype.rs b/src/librustc_traits/type_op_subtype.rs deleted file mode 100644 index 30a8550f107b2..0000000000000 --- a/src/librustc_traits/type_op_subtype.rs +++ /dev/null @@ -1,35 +0,0 @@ -// 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::traits::query::type_op::subtype::Subtype; -use rustc::traits::query::NoSolution; -use rustc::traits::ObligationCause; -use rustc::ty::TyCtxt; -use rustc_data_structures::sync::Lrc; - -crate fn type_op_subtype<'tcx>( - tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Subtype<'tcx>>, -) -> Result>>, NoSolution> { - tcx.infer_ctxt().enter_canonical_trait_query( - &canonicalized, - |infcx, - Subtype { - param_env, - sub, - sup, - }| { - Ok(infcx - .at(&ObligationCause::dummy(), param_env) - .sup(sup, sub)?) - }, - ) -} From d69551243756fff4c81d857177bffbb0f6fcb9ff Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 06:57:20 -0400 Subject: [PATCH 59/72] fix wrong query description --- src/librustc/ty/query/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index 8a48abd093d7f..eadfc62244f81 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -113,7 +113,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::type_op_eq<'tcx> { impl<'tcx> QueryDescription<'tcx> for queries::type_op_subtype<'tcx> { fn describe(_tcx: TyCtxt, goal: CanonicalTypeOpSubtypeGoal<'tcx>) -> String { - format!("evaluating `type_op_eq` `{:?}`", goal) + format!("evaluating `type_op_subtype` `{:?}`", goal) } } From 59ea17ed3f598791e241c7e7629c4d67f51d436e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 07:02:37 -0400 Subject: [PATCH 60/72] rename `upcast` to `cast_to_tcx_lifetime` and improve comment --- src/librustc/traits/query/type_op/eq.rs | 2 +- src/librustc/traits/query/type_op/mod.rs | 15 ++++++++++----- src/librustc/traits/query/type_op/normalize.rs | 14 +++++++------- src/librustc/traits/query/type_op/outlives.rs | 2 +- .../traits/query/type_op/prove_predicate.rs | 2 +- src/librustc/traits/query/type_op/subtype.rs | 2 +- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 04d256a93e12a..fc5d174fff4b9 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -48,7 +48,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { tcx.type_op_eq(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, ()>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { v diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 16280885c12ad..5875849f4f925 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -54,14 +54,19 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { canonicalized: Canonicalized<'gcx, Self::QueryKey>, ) -> Fallible>; - /// "Upcasts" a lifted query result (which is in the gcx lifetime) + /// Casts a lifted query result (which is in the gcx lifetime) /// into the tcx lifetime. This is always just an identity cast, - /// but the generic code does't realize it, so we have to push the - /// operation into the impls that know more specifically what + /// but the generic code doesn't realize it -- put another way, in + /// the generic code, we have a `Lifted<'gcx, Self::QueryResult>` + /// and we want to convert that to a `Self::QueryResult`. This is + /// not a priori valid, so we can't do it -- but in practice, it + /// is always a no-op (e.g., the lifted form of a type, + /// `Ty<'gcx>`, is a subtype of `Ty<'tcx>`). So we have to push + /// the operation into the impls that know more specifically what /// `QueryResult` is. This operation would (maybe) be nicer with /// something like HKTs or GATs, since then we could make /// `QueryResult` parametric and `'gcx` and `'tcx` etc. - fn upcast_result( + fn cast_to_tcx_lifetime( lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>; @@ -80,7 +85,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; - let canonical_result = Self::upcast_result(&canonical_result); + let canonical_result = Self::cast_to_tcx_lifetime(&canonical_result); let param_env = Self::param_env(&query_key); diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index b72c887ba50c1..5e3a23a5f305f 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -55,10 +55,10 @@ where T::type_op_method(tcx, canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, T>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, T>> { - T::upcast_result(v) + T::cast_to_tcx_lifetime(v) } } @@ -70,7 +70,7 @@ pub trait Normalizable<'gcx, 'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx> /// Convert from the `'gcx` (lifted) form of `Self` into the `tcx` /// form of `Self`. - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>>; } @@ -86,7 +86,7 @@ where tcx.type_op_normalize_ty(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v @@ -104,7 +104,7 @@ where tcx.type_op_normalize_predicate(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v @@ -122,7 +122,7 @@ where tcx.type_op_normalize_poly_fn_sig(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v @@ -140,7 +140,7 @@ where tcx.type_op_normalize_fn_sig(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index 0254b901a8c58..d429cccca96e2 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -55,7 +55,7 @@ where tcx.dropck_outlives(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>> { lifted_query_result diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index b06ad32ff2232..c610421e68a90 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -46,7 +46,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { tcx.type_op_prove_predicate(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, ()>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { v diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index bdc7dd9589b67..8bb773b4c80d1 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -52,7 +52,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { tcx.type_op_subtype(canonicalized) } - fn upcast_result( + fn cast_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, ()>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { v From 5bfdb97684546bb3716e11fbd47ebfa8703eb5e5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 07:05:40 -0400 Subject: [PATCH 61/72] remove `either` dependency we are not using --- src/Cargo.lock | 1 - src/librustc/Cargo.toml | 1 - src/librustc/lib.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index f8551dffcf4bf..b74587e566210 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1781,7 +1781,6 @@ dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "chalk-engine 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "fmt_macros 0.0.0", "graphviz 0.0.0", diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 542ee9f02c909..457a9f2f625ec 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -11,7 +11,6 @@ crate-type = ["dylib"] [dependencies] arena = { path = "../libarena" } bitflags = "1.0" -either = "1.5.0" fmt_macros = { path = "../libfmt_macros" } graphviz = { path = "../libgraphviz" } jobserver = "0.1" diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 7c3d3414846de..102efe2bef3f7 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -79,7 +79,6 @@ extern crate arena; #[macro_use] extern crate bitflags; extern crate core; -extern crate either; extern crate fmt_macros; extern crate getopts; extern crate graphviz; From 1be4fffc24f0e348832bac2ed79d65febaf648bd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 07:19:17 -0400 Subject: [PATCH 62/72] improve comment on instantiate NLL query result fn --- src/librustc/infer/canonical/query_result.rs | 48 ++++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 9615ee2d3b198..fae624b38a8e8 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -206,16 +206,44 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { }) } - /// NLL does a lot of queries that have a particular form that we - /// can take advantage of to be more efficient. These queries do - /// not have any *type* inference variables, only region inference - /// variables. Therefore, when we instantiate the query result, we - /// only ever produce new *region constraints* and never other - /// forms of obligations (moreover, since we only determine - /// satisfiability modulo region constraints, instantiation is - /// infallible). Therefore, the return value need only be a larger - /// set of query region constraints. These constraints can then be - /// added directly to the NLL inference context. + /// An alternative to + /// `instantiate_query_result_and_region_obligations` that is more + /// efficient for NLL. NLL is a bit more advanced in the + /// "transition to chalk" than the rest of the compiler. During + /// the NLL type check, all of the "processing" of types and + /// things happens in queries -- the NLL checker itself is only + /// interested in the region obligations (`'a: 'b` or `T: 'b`) + /// that come out of these queries, which it wants to convert into + /// MIR-based constraints and solve. Therefore, it is most + /// convenient for the NLL Type Checker to **directly consume** + /// the `QueryRegionConstraint` values that arise from doing a + /// query. This is contrast to other parts of the compiler, which + /// would prefer for those `QueryRegionConstraint` to be converted + /// into the older infcx-style constraints (e.g., calls to + /// [`sub_regions()`] or [`register_region_obligation()`]). + /// + /// Therefore, `instantiate_nll_query_result_and_region_obligations` performs the same + /// basic operations as `instantiate_query_result_and_region_obligations` but + /// it returns its result differently: + /// + /// - It creates a substitution `S` that maps from the original + /// query variables to the values computed in the query + /// result. If any errors arise, they are propagated back as an + /// `Err` result. + /// - In the case of a successful substitution, we will append + /// `QueryRegionConstraint` values onto the + /// `output_query_region_constraints` vector for the solver to + /// use (if an error arises, some values may also be pushed, but + /// they should be ignored). + /// - It **can happen** (though it rarely does currently) that + /// equating types and things will give rise to subobligations + /// that must be processed. In this case, those subobligations + /// are propagated back in the return value. + /// - Finally, the query result (of type `R`) is propagated back, + /// after applying the substitution `S`. + /// + /// [`register_region_obligation()`: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/infer/struct.InferCtxt.html#method.register_region_obligation + /// [`sub_regions()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/infer/struct.InferCtxt.html#method.sub_regions pub fn instantiate_nll_query_result_and_region_obligations( &self, cause: &ObligationCause<'tcx>, From c6a7c6fc68049a1c7e862c595f326dedbc4e7ae6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 07:26:29 -0400 Subject: [PATCH 63/72] improve comments on `dropck_outlives` --- src/librustc/traits/query/type_op/mod.rs | 6 ++++++ src/librustc/traits/query/type_op/outlives.rs | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 5875849f4f925..b55c0de5b0bc0 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -49,6 +49,12 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx>; + /// Performs the actual query with the canonicalized key -- the + /// real work happens here. This method is not given an `infcx` + /// because it shouldn't need one -- and if it had access to one, + /// it might do things like invoke `sub_regions`, which would be + /// bad, because it would create subregion relationships that are + /// not captured in the return value. fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Self::QueryKey>, diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index d429cccca96e2..369cefdfb4740 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -52,6 +52,15 @@ where tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, Self::QueryKey>, ) -> Fallible> { + // Subtle: note that we are not invoking + // `infcx.at(...).dropck_outlives(...)` here, but rather the + // underlying `dropck_outlives` query. This same underlying + // query is also used by the + // `infcx.at(...).dropck_outlives(...)` fn. Avoiding the + // wrapper means we don't need an infcx in this code, which is + // good because the interface doesn't give us one (so that we + // know we are not registering any subregion relations or + // other things). tcx.dropck_outlives(canonicalized) } From 35a50655731efdac281a9cff1cf4f303549a5b02 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 07:28:25 -0400 Subject: [PATCH 64/72] pacify the mercilous tidy --- src/librustc/infer/canonical/query_result.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index fae624b38a8e8..ccb43c1fcdf7b 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -220,7 +220,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// query. This is contrast to other parts of the compiler, which /// would prefer for those `QueryRegionConstraint` to be converted /// into the older infcx-style constraints (e.g., calls to - /// [`sub_regions()`] or [`register_region_obligation()`]). + /// `sub_regions` or `register_region_obligation`). /// /// Therefore, `instantiate_nll_query_result_and_region_obligations` performs the same /// basic operations as `instantiate_query_result_and_region_obligations` but @@ -241,9 +241,6 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// are propagated back in the return value. /// - Finally, the query result (of type `R`) is propagated back, /// after applying the substitution `S`. - /// - /// [`register_region_obligation()`: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/infer/struct.InferCtxt.html#method.register_region_obligation - /// [`sub_regions()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/infer/struct.InferCtxt.html#method.sub_regions pub fn instantiate_nll_query_result_and_region_obligations( &self, cause: &ObligationCause<'tcx>, From c2f7757bc5682935555850375af66285d4bcd22f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 08:55:41 -0400 Subject: [PATCH 65/72] extend comment to note complications around lifetimes --- src/librustc/infer/canonical/query_result.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index ccb43c1fcdf7b..a4e33b7d86851 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -46,6 +46,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { /// canonical result created. /// /// Returns `NoSolution` in the event of any error. + /// + /// (It might be mildly nicer to implement this on `TyCtxt`, and + /// not `InferCtxtBuilder`, but that is a bit tricky right now. + /// In part because we would need a `for<'gcx: 'tcx>` sort of + /// bound for the closure and in part because it is convenient to + /// have `'tcx` be free on this function so that we can talk about + /// `K: TypeFoldable<'tcx>`.) pub fn enter_canonical_trait_query( &'tcx mut self, canonical_key: &Canonical<'tcx, K>, From c3f7e02fb6ba3baa30bbbcf373f64ce0c48b1893 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 09:27:07 -0400 Subject: [PATCH 66/72] rename to `shrink_to_tcx_lifetime` --- src/librustc/traits/query/type_op/eq.rs | 2 +- src/librustc/traits/query/type_op/mod.rs | 4 ++-- src/librustc/traits/query/type_op/normalize.rs | 14 +++++++------- src/librustc/traits/query/type_op/outlives.rs | 2 +- .../traits/query/type_op/prove_predicate.rs | 2 +- src/librustc/traits/query/type_op/subtype.rs | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index fc5d174fff4b9..7d05d0a2656f3 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -48,7 +48,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { tcx.type_op_eq(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, ()>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { v diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index b55c0de5b0bc0..d89d31726cb53 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -72,7 +72,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { /// `QueryResult` is. This operation would (maybe) be nicer with /// something like HKTs or GATs, since then we could make /// `QueryResult` parametric and `'gcx` and `'tcx` etc. - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>; @@ -91,7 +91,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; - let canonical_result = Self::cast_to_tcx_lifetime(&canonical_result); + let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result); let param_env = Self::param_env(&query_key); diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index 5e3a23a5f305f..e4d27bccba3f9 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -55,10 +55,10 @@ where T::type_op_method(tcx, canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, T>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, T>> { - T::cast_to_tcx_lifetime(v) + T::shrink_to_tcx_lifetime(v) } } @@ -70,7 +70,7 @@ pub trait Normalizable<'gcx, 'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx> /// Convert from the `'gcx` (lifted) form of `Self` into the `tcx` /// form of `Self`. - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>>; } @@ -86,7 +86,7 @@ where tcx.type_op_normalize_ty(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v @@ -104,7 +104,7 @@ where tcx.type_op_normalize_predicate(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v @@ -122,7 +122,7 @@ where tcx.type_op_normalize_poly_fn_sig(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v @@ -140,7 +140,7 @@ where tcx.type_op_normalize_fn_sig(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, Self>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self>> { v diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index 369cefdfb4740..cfaed689126ef 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -64,7 +64,7 @@ where tcx.dropck_outlives(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>> { lifted_query_result diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index c610421e68a90..f54710c4805a1 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -46,7 +46,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { tcx.type_op_prove_predicate(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, ()>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { v diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index 8bb773b4c80d1..e1589e1a9b68f 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -52,7 +52,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { tcx.type_op_subtype(canonicalized) } - fn cast_to_tcx_lifetime( + fn shrink_to_tcx_lifetime( v: &'a CanonicalizedQueryResult<'gcx, ()>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, ()>> { v From b2e899f843fd94ed3188f4feceb84c74039a30e2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 09:42:00 -0400 Subject: [PATCH 67/72] move into `provide` methods --- src/librustc_traits/dropck_outlives.rs | 11 +++++++- src/librustc_traits/evaluate_obligation.rs | 10 ++++++- src/librustc_traits/lib.rs | 24 ++++------------ src/librustc_traits/lowering.rs | 9 ++++++ .../normalize_erasing_regions.rs | 10 ++++++- .../normalize_projection_ty.rs | 10 ++++++- src/librustc_traits/type_op.rs | 28 ++++++++++++++----- 7 files changed, 73 insertions(+), 29 deletions(-) diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index cd49aa2241b3c..eddfee95c8f37 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -13,13 +13,22 @@ use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::traits::query::dropck_outlives::{DropckOutlivesResult, DtorckConstraint}; use rustc::traits::query::{CanonicalTyGoal, NoSolution}; use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; +use rustc::ty::query::Providers; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc::util::nodemap::FxHashSet; use rustc_data_structures::sync::Lrc; use syntax::codemap::{Span, DUMMY_SP}; -crate fn dropck_outlives<'tcx>( +crate fn provide(p: &mut Providers) { + *p = Providers { + dropck_outlives, + adt_dtorck_constraint, + ..*p + }; +} + +fn dropck_outlives<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, goal: CanonicalTyGoal<'tcx>, ) -> Result>>>, NoSolution> { diff --git a/src/librustc_traits/evaluate_obligation.rs b/src/librustc_traits/evaluate_obligation.rs index 21259bbcd38ff..e8a3447902fd3 100644 --- a/src/librustc_traits/evaluate_obligation.rs +++ b/src/librustc_traits/evaluate_obligation.rs @@ -11,10 +11,18 @@ use rustc::traits::{EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode}; use rustc::traits::query::CanonicalPredicateGoal; +use rustc::ty::query::Providers; use rustc::ty::{ParamEnvAnd, TyCtxt}; use syntax::codemap::DUMMY_SP; -crate fn evaluate_obligation<'tcx>( +crate fn provide(p: &mut Providers) { + *p = Providers { + evaluate_obligation, + ..*p + }; +} + +fn evaluate_obligation<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, goal: CanonicalPredicateGoal<'tcx>, ) -> Result { diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index b02d7f6c3f7b6..1da3907915a07 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -39,22 +39,10 @@ mod type_op; use rustc::ty::query::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, - normalize_ty_after_erasing_regions: - normalize_erasing_regions::normalize_ty_after_erasing_regions, - program_clauses_for: lowering::program_clauses_for, - program_clauses_for_env: lowering::program_clauses_for_env, - evaluate_obligation: evaluate_obligation::evaluate_obligation, - type_op_eq: type_op::type_op_eq, - type_op_prove_predicate: type_op::type_op_prove_predicate, - type_op_subtype: type_op::type_op_subtype, - type_op_normalize_ty: type_op::type_op_normalize_ty, - type_op_normalize_predicate: type_op::type_op_normalize_predicate, - type_op_normalize_fn_sig: type_op::type_op_normalize_fn_sig, - type_op_normalize_poly_fn_sig: type_op::type_op_normalize_poly_fn_sig, - ..*p - }; + dropck_outlives::provide(p); + evaluate_obligation::provide(p); + lowering::provide(p); + normalize_projection_ty::provide(p); + normalize_erasing_regions::provide(p); + type_op::provide(p); } diff --git a/src/librustc_traits/lowering.rs b/src/librustc_traits/lowering.rs index 0270e970976ea..16aa63d699997 100644 --- a/src/librustc_traits/lowering.rs +++ b/src/librustc_traits/lowering.rs @@ -14,6 +14,7 @@ use rustc::hir::map::definitions::DefPathData; use rustc::hir::{self, ImplPolarity}; use rustc::traits::{Clause, Clauses, DomainGoal, Goal, PolyDomainGoal, ProgramClause, WhereClause, FromEnv, WellFormed}; +use rustc::ty::query::Providers; use rustc::ty::subst::Substs; use rustc::ty::{self, Slice, TyCtxt}; use rustc_data_structures::fx::FxHashSet; @@ -22,6 +23,14 @@ use syntax::ast; use std::iter; +crate fn provide(p: &mut Providers) { + *p = Providers { + program_clauses_for, + program_clauses_for_env, + ..*p + }; +} + crate trait Lower { /// Lower a rustc construct (e.g. `ty::TraitPredicate`) to a chalk-like type. fn lower(&self) -> T; diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs index 299433d479dc6..a85983d0e9a81 100644 --- a/src/librustc_traits/normalize_erasing_regions.rs +++ b/src/librustc_traits/normalize_erasing_regions.rs @@ -10,10 +10,18 @@ use rustc::traits::{Normalized, ObligationCause}; use rustc::traits::query::NoSolution; +use rustc::ty::query::Providers; use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use std::sync::atomic::Ordering; -crate fn normalize_ty_after_erasing_regions<'tcx>( +crate fn provide(p: &mut Providers) { + *p = Providers { + normalize_ty_after_erasing_regions, + ..*p + }; +} + +fn normalize_ty_after_erasing_regions<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, goal: ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index 473b2c8e99850..1a8899ad8e03f 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -12,13 +12,21 @@ use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::infer::InferOk; use rustc::traits::query::{normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution}; use rustc::traits::{self, ObligationCause, SelectionContext}; +use rustc::ty::query::Providers; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; use std::sync::atomic::Ordering; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::DUMMY_SP; -crate fn normalize_projection_ty<'tcx>( +crate fn provide(p: &mut Providers) { + *p = Providers { + normalize_projection_ty, + ..*p + }; +} + +fn normalize_projection_ty<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, goal: CanonicalProjectionGoal<'tcx>, ) -> Result>>>, NoSolution> { diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index f44cd686b40a3..9e8640a32de5b 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -16,11 +16,25 @@ use rustc::traits::query::type_op::prove_predicate::ProvePredicate; use rustc::traits::query::type_op::subtype::Subtype; use rustc::traits::query::{Fallible, NoSolution}; use rustc::traits::{Obligation, Normalized, ObligationCause}; +use rustc::ty::query::Providers; use rustc::ty::{FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::Lrc; use std::fmt; -crate fn type_op_eq<'tcx>( +crate fn provide(p: &mut Providers) { + *p = Providers { + type_op_eq, + type_op_prove_predicate, + type_op_subtype, + type_op_normalize_ty, + type_op_normalize_predicate, + type_op_normalize_fn_sig, + type_op_normalize_poly_fn_sig, + ..*p + }; +} + +fn type_op_eq<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Eq<'tcx>>, ) -> Result>>, NoSolution> { @@ -44,7 +58,7 @@ where Ok(InferOk { value, obligations }) // ugh we should merge these two structs } -crate fn type_op_normalize_ty( +fn type_op_normalize_ty( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, Ty<'tcx>>>, ) -> Result>>>, NoSolution> { @@ -52,7 +66,7 @@ crate fn type_op_normalize_ty( .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_normalize_predicate( +fn type_op_normalize_predicate( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, Predicate<'tcx>>>, ) -> Result>>>, NoSolution> { @@ -60,7 +74,7 @@ crate fn type_op_normalize_predicate( .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_normalize_fn_sig( +fn type_op_normalize_fn_sig( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, FnSig<'tcx>>>, ) -> Result>>>, NoSolution> { @@ -68,7 +82,7 @@ crate fn type_op_normalize_fn_sig( .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_normalize_poly_fn_sig( +fn type_op_normalize_poly_fn_sig( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Normalize<'tcx, PolyFnSig<'tcx>>>, ) -> Result>>>, NoSolution> { @@ -76,7 +90,7 @@ crate fn type_op_normalize_poly_fn_sig( .enter_canonical_trait_query(&canonicalized, type_op_normalize) } -crate fn type_op_subtype<'tcx>( +fn type_op_subtype<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, Subtype<'tcx>>, ) -> Result>>, NoSolution> { @@ -95,7 +109,7 @@ crate fn type_op_subtype<'tcx>( ) } -crate fn type_op_prove_predicate<'tcx>( +fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, ProvePredicate<'tcx>>, ) -> Result>>, NoSolution> { From 0a0dae0964ee64c9c095f0ab891f0df4a412a253 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 15:17:03 -0400 Subject: [PATCH 68/72] pull out `ParamEnvAnd` and remove `QueryKey` --- src/librustc/traits/query/mod.rs | 8 +- src/librustc/traits/query/type_op/eq.rs | 28 ++--- src/librustc/traits/query/type_op/mod.rs | 101 +++++++++--------- .../traits/query/type_op/normalize.rs | 52 ++++----- src/librustc/traits/query/type_op/outlives.rs | 64 +++++++---- .../traits/query/type_op/prove_predicate.rs | 23 ++-- src/librustc/traits/query/type_op/subtype.rs | 27 ++--- .../borrow_check/nll/type_check/liveness.rs | 5 +- .../borrow_check/nll/type_check/mod.rs | 8 +- src/librustc_traits/type_op.rs | 37 +++---- 10 files changed, 172 insertions(+), 181 deletions(-) diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 96034dd935ca8..54b67edb1360b 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -34,16 +34,16 @@ pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; pub type CanonicalTypeOpEqGoal<'tcx> = - Canonical<'tcx, type_op::eq::Eq<'tcx>>; + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::eq::Eq<'tcx>>>; pub type CanonicalTypeOpSubtypeGoal<'tcx> = - Canonical<'tcx, type_op::subtype::Subtype<'tcx>>; + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::subtype::Subtype<'tcx>>>; pub type CanonicalTypeOpProvePredicateGoal<'tcx> = - Canonical<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>; + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>; pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = - Canonical<'tcx, type_op::normalize::Normalize<'tcx, T>>; + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::normalize::Normalize>>; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 7d05d0a2656f3..7c35f448a312e 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -8,42 +8,36 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; +use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; use traits::query::Fallible; -use ty::{ParamEnv, Ty, TyCtxt}; +use ty::{ParamEnvAnd, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Eq<'tcx> { - pub param_env: ParamEnv<'tcx>, pub a: Ty<'tcx>, pub b: Ty<'tcx>, } impl<'tcx> Eq<'tcx> { - pub fn new(param_env: ParamEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Self { - Self { param_env, a, b } + pub fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + Self { a, b } } } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { - type QueryKey = Self; type QueryResult = (); - fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if self.a == self.b { - Ok(()) + fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Eq<'tcx>>) -> Option { + if key.value.a == key.value.b { + Some(()) } else { - Err(self) + None } } - fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { - key.param_env - } - fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonical<'gcx, Eq<'gcx>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { tcx.type_op_eq(canonicalized) } @@ -57,7 +51,6 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for Eq<'tcx> { - param_env, a, b, } @@ -66,12 +59,11 @@ BraceStructTypeFoldableImpl! { BraceStructLiftImpl! { impl<'a, 'tcx> Lift<'tcx> for Eq<'a> { type Lifted = Eq<'tcx>; - param_env, a, b, } } impl_stable_hash_for! { - struct Eq<'tcx> { param_env, a, b } + struct Eq<'tcx> { a, b } } diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index d89d31726cb53..191fc1aabb17f 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -8,22 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{ - Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, QueryResult, -}; +use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, + QueryResult}; use infer::{InferCtxt, InferOk}; use std::fmt; use std::rc::Rc; use traits::query::Fallible; use traits::ObligationCause; use ty::fold::TypeFoldable; -use ty::{Lift, ParamEnv, TyCtxt}; +use ty::{Lift, ParamEnvAnd, TyCtxt}; pub mod custom; pub mod eq; pub mod normalize; pub mod outlives; pub mod prove_predicate; +use self::prove_predicate::ProvePredicate; pub mod subtype; pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { @@ -38,16 +38,18 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { ) -> Fallible<(Self::Output, Option>>>)>; } -pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { - type QueryKey: TypeFoldable<'tcx> + Lift<'gcx>; +pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: + fmt::Debug + Sized + TypeFoldable<'tcx> + Lift<'gcx> +{ type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; /// Either converts `self` directly into a `QueryResult` (for /// simple cases) or into a `QueryKey` (for more complex cases /// where we actually have work to do). - fn prequery(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result; - - fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx>; + fn prequery( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option; /// Performs the actual query with the canonicalized key -- the /// real work happens here. This method is not given an `infcx` @@ -57,7 +59,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { /// not captured in the return value. fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Self::QueryKey>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible>; /// Casts a lifted query result (which is in the gcx lifetime) @@ -77,52 +79,53 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>; fn fully_perform_into( - self, + query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'gcx, 'tcx>, output_query_region_constraints: &mut Vec>, ) -> Fallible { - match QueryTypeOp::prequery(self, infcx.tcx) { - Ok(result) => Ok(result), - Err(query_key) => { - // FIXME(#33684) -- We need to use - // `canonicalize_hr_query_hack` here because of things - // like the subtype query, which go awry around - // `'static` otherwise. - let (canonical_self, canonical_var_values) = - infcx.canonicalize_hr_query_hack(&query_key); - let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; - let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result); - - let param_env = Self::param_env(&query_key); - - let InferOk { value, obligations } = infcx - .instantiate_nll_query_result_and_region_obligations( - &ObligationCause::dummy(), - param_env, - &canonical_var_values, - canonical_result, - output_query_region_constraints, - )?; - - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - for obligation in obligations { - let () = prove_predicate::ProvePredicate::new( - obligation.param_env, - obligation.predicate, - ).fully_perform_into(infcx, output_query_region_constraints)?; - } - - Ok(value) - } + if let Some(result) = QueryTypeOp::prequery(infcx.tcx, &query_key) { + return Ok(result); } + + // FIXME(#33684) -- We need to use + // `canonicalize_hr_query_hack` here because of things + // like the subtype query, which go awry around + // `'static` otherwise. + let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); + let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; + let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result); + + let param_env = query_key.param_env; + + let InferOk { value, obligations } = infcx + .instantiate_nll_query_result_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + output_query_region_constraints, + )?; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + for obligation in obligations { + let () = ProvePredicate::fully_perform_into( + obligation + .param_env + .and(ProvePredicate::new(obligation.predicate)), + infcx, + output_query_region_constraints, + )?; + } + + Ok(value) } } -impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for Q +impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for ParamEnvAnd<'tcx, Q> where Q: QueryTypeOp<'gcx, 'tcx>, { diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index e4d27bccba3f9..3491b0b4b4ece 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -12,45 +12,39 @@ use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, Query use std::fmt; use traits::query::Fallible; use ty::fold::TypeFoldable; -use ty::{self, Lift, ParamEnv, Ty, TyCtxt}; +use ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct Normalize<'tcx, T> { - pub param_env: ParamEnv<'tcx>, +pub struct Normalize { pub value: T, } -impl<'tcx, T> Normalize<'tcx, T> +impl<'tcx, T> Normalize where T: fmt::Debug + TypeFoldable<'tcx>, { - pub fn new(param_env: ParamEnv<'tcx>, value: T) -> Self { - Self { param_env, value } + pub fn new(value: T) -> Self { + Self { value } } } -impl<'gcx: 'tcx, 'tcx, T> super::QueryTypeOp<'gcx, 'tcx> for Normalize<'tcx, T> +impl<'gcx: 'tcx, 'tcx, T> super::QueryTypeOp<'gcx, 'tcx> for Normalize where T: Normalizable<'gcx, 'tcx>, { - type QueryKey = Self; type QueryResult = T; - fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if !self.value.has_projections() { - Ok(self.value) + fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { + if !key.value.value.has_projections() { + Some(key.value.value) } else { - Err(self) + None } } - fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { - key.param_env - } - fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Self>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { T::type_op_method(tcx, canonicalized) } @@ -62,10 +56,10 @@ where } } -pub trait Normalizable<'gcx, 'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx> { +pub trait Normalizable<'gcx, 'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx> + Copy { fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Fallible>; /// Convert from the `'gcx` (lifted) form of `Self` into the `tcx` @@ -81,7 +75,7 @@ where { fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Fallible> { tcx.type_op_normalize_ty(canonicalized) } @@ -99,7 +93,7 @@ where { fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Fallible> { tcx.type_op_normalize_predicate(canonicalized) } @@ -117,7 +111,7 @@ where { fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Fallible> { tcx.type_op_normalize_poly_fn_sig(canonicalized) } @@ -135,7 +129,7 @@ where { fn type_op_method( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Normalize<'gcx, Self>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Fallible> { tcx.type_op_normalize_fn_sig(canonicalized) } @@ -148,22 +142,20 @@ where } BraceStructTypeFoldableImpl! { - impl<'tcx, T> TypeFoldable<'tcx> for Normalize<'tcx, T> { - param_env, + impl<'tcx, T> TypeFoldable<'tcx> for Normalize { value, } where T: TypeFoldable<'tcx>, } BraceStructLiftImpl! { - impl<'a, 'tcx, T> Lift<'tcx> for Normalize<'a, T> { - type Lifted = Normalize<'tcx, T::Lifted>; - param_env, + impl<'tcx, T> Lift<'tcx> for Normalize { + type Lifted = Normalize; value, } where T: Lift<'tcx>, } impl_stable_hash_for! { - impl<'tcx, T> for struct Normalize<'tcx, T> { - param_env, value + impl<'tcx, T> for struct Normalize { + value } } diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index cfaed689126ef..bfb476055c825 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -12,20 +12,16 @@ use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, Query use traits::query::dropck_outlives::trivial_dropck_outlives; use traits::query::dropck_outlives::DropckOutlivesResult; use traits::query::Fallible; -use ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; +use ty::{ParamEnvAnd, Ty, TyCtxt}; -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub struct DropckOutlives<'tcx> { - param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>, } impl<'tcx> DropckOutlives<'tcx> { - pub fn new(param_env: ParamEnv<'tcx>, dropped_ty: Ty<'tcx>) -> Self { - DropckOutlives { - param_env, - dropped_ty, - } + pub fn new(dropped_ty: Ty<'tcx>) -> Self { + DropckOutlives { dropped_ty } } } @@ -33,24 +29,22 @@ impl super::QueryTypeOp<'gcx, 'tcx> for DropckOutlives<'tcx> where 'gcx: 'tcx, { - type QueryKey = ParamEnvAnd<'tcx, Ty<'tcx>>; type QueryResult = DropckOutlivesResult<'tcx>; - fn prequery(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - if trivial_dropck_outlives(tcx, self.dropped_ty) { - Ok(DropckOutlivesResult::default()) + fn prequery( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + if trivial_dropck_outlives(tcx, key.value.dropped_ty) { + Some(DropckOutlivesResult::default()) } else { - Err(self.param_env.and(self.dropped_ty)) + None } } - fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { - key.param_env - } - fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonicalized<'gcx, Self::QueryKey>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { // Subtle: note that we are not invoking // `infcx.at(...).dropck_outlives(...)` here, but rather the @@ -61,6 +55,23 @@ where // good because the interface doesn't give us one (so that we // know we are not registering any subregion relations or // other things). + + // FIXME convert to the type expected by the `dropck_outlives` + // query. This should eventually be fixed by changing the + // *underlying query*. + let Canonical { + variables, + value: + ParamEnvAnd { + param_env, + value: DropckOutlives { dropped_ty }, + }, + } = canonicalized; + let canonicalized = Canonical { + variables, + value: param_env.and(dropped_ty), + }; + tcx.dropck_outlives(canonicalized) } @@ -70,3 +81,20 @@ where lifted_query_result } } + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for DropckOutlives<'tcx> { + dropped_ty + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for DropckOutlives<'a> { + type Lifted = DropckOutlives<'tcx>; + dropped_ty + } +} + +impl_stable_hash_for! { + struct DropckOutlives<'tcx> { dropped_ty } +} diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index f54710c4805a1..dfe640f7431fc 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -8,40 +8,33 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; +use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; use traits::query::Fallible; -use ty::{ParamEnv, Predicate, TyCtxt}; +use ty::{ParamEnvAnd, Predicate, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct ProvePredicate<'tcx> { - pub param_env: ParamEnv<'tcx>, pub predicate: Predicate<'tcx>, } impl<'tcx> ProvePredicate<'tcx> { - pub fn new(param_env: ParamEnv<'tcx>, predicate: Predicate<'tcx>) -> Self { + pub fn new(predicate: Predicate<'tcx>) -> Self { ProvePredicate { - param_env, predicate, } } } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { - type QueryKey = Self; type QueryResult = (); - fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result { - Err(self) - } - - fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { - key.param_env + fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, _key: &ParamEnvAnd<'tcx, Self>) -> Option { + None } fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonical<'gcx, ProvePredicate<'gcx>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { tcx.type_op_prove_predicate(canonicalized) } @@ -55,7 +48,6 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for ProvePredicate<'tcx> { - param_env, predicate, } } @@ -63,11 +55,10 @@ BraceStructTypeFoldableImpl! { BraceStructLiftImpl! { impl<'a, 'tcx> Lift<'tcx> for ProvePredicate<'a> { type Lifted = ProvePredicate<'tcx>; - param_env, predicate, } } impl_stable_hash_for! { - struct ProvePredicate<'tcx> { param_env, predicate } + struct ProvePredicate<'tcx> { predicate } } diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index e1589e1a9b68f..db535e3dd13db 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -8,21 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; +use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; use traits::query::Fallible; -use ty::{ParamEnv, Ty, TyCtxt}; +use ty::{ParamEnvAnd, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Subtype<'tcx> { - pub param_env: ParamEnv<'tcx>, pub sub: Ty<'tcx>, pub sup: Ty<'tcx>, } impl<'tcx> Subtype<'tcx> { - pub fn new(param_env: ParamEnv<'tcx>, sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + pub fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { Self { - param_env, sub, sup, } @@ -30,24 +28,19 @@ impl<'tcx> Subtype<'tcx> { } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { - type QueryKey = Self; type QueryResult = (); - fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self::QueryKey> { - if self.sub == self.sup { - Ok(()) + fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { + if key.value.sub == key.value.sup { + Some(()) } else { - Err(self) + None } } - fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { - key.param_env - } - fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, - canonicalized: Canonical<'gcx, Subtype<'gcx>>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { tcx.type_op_subtype(canonicalized) } @@ -61,7 +54,6 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for Subtype<'tcx> { - param_env, sub, sup, } @@ -70,12 +62,11 @@ BraceStructTypeFoldableImpl! { BraceStructLiftImpl! { impl<'a, 'tcx> Lift<'tcx> for Subtype<'a> { type Lifted = Subtype<'tcx>; - param_env, sub, sup, } } impl_stable_hash_for! { - struct Subtype<'tcx> { param_env, sub, sup } + struct Subtype<'tcx> { sub, sup } } 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 73ed695bfb276..f27de92c6215a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -13,10 +13,10 @@ use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; -use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::infer::canonical::QueryRegionConstraint; use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::traits::query::type_op::outlives::DropckOutlives; use rustc::traits::query::type_op::TypeOp; use rustc::ty::{Ty, TypeFoldable}; @@ -223,7 +223,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); let param_env = cx.param_env; - let (dropck_result, region_constraint_data) = DropckOutlives::new(param_env, dropped_ty) + let (dropck_result, region_constraint_data) = param_env + .and(DropckOutlives::new(dropped_ty)) .fully_perform(cx.infcx) .unwrap(); diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 843fadf62272b..39f6c6a686426 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -778,13 +778,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.fully_perform_op( locations, - type_op::subtype::Subtype::new(param_env, sub, sup), + param_env.and(type_op::subtype::Subtype::new(sub, sup)), ) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> { let param_env = self.param_env; - self.fully_perform_op(locations, type_op::eq::Eq::new(param_env, b, a)) + self.fully_perform_op(locations, param_env.and(type_op::eq::Eq::new(b, a))) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { @@ -1576,7 +1576,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.fully_perform_op( location.at_self(), - type_op::prove_predicate::ProvePredicate::new(param_env, predicate), + param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)), ).unwrap_or_else(|NoSolution| { span_mirbug!(self, NoSolution, "could not prove {:?}", predicate); }) @@ -1616,7 +1616,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.fully_perform_op( location.to_locations(), - type_op::normalize::Normalize::new(param_env, value), + param_env.and(type_op::normalize::Normalize::new(value)), ).unwrap_or_else(|NoSolution| { span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value); value diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index 9e8640a32de5b..9940608222230 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -17,7 +17,7 @@ use rustc::traits::query::type_op::subtype::Subtype; use rustc::traits::query::{Fallible, NoSolution}; use rustc::traits::{Obligation, Normalized, ObligationCause}; use rustc::ty::query::Providers; -use rustc::ty::{FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{ParamEnvAnd, FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::Lrc; use std::fmt; @@ -36,22 +36,23 @@ crate fn provide(p: &mut Providers) { fn type_op_eq<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Eq<'tcx>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Eq<'tcx>>>, ) -> Result>>, NoSolution> { tcx.infer_ctxt() - .enter_canonical_trait_query(&canonicalized, |infcx, Eq { param_env, a, b }| { + .enter_canonical_trait_query(&canonicalized, |infcx, key| { + let (param_env, Eq { a, b }) = key.into_parts(); Ok(infcx.at(&ObligationCause::dummy(), param_env).eq(a, b)?) }) } fn type_op_normalize( infcx: &InferCtxt<'_, 'gcx, 'tcx>, - key: Normalize<'tcx, T>, + key: ParamEnvAnd<'tcx, Normalize>, ) -> Fallible> where T: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx>, { - let Normalize { param_env, value } = key; + let (param_env, Normalize { value }) = key.into_parts(); let Normalized { value, obligations } = infcx .at(&ObligationCause::dummy(), param_env) .normalize(&value)?; @@ -60,7 +61,7 @@ where fn type_op_normalize_ty( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Normalize<'tcx, Ty<'tcx>>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() .enter_canonical_trait_query(&canonicalized, type_op_normalize) @@ -68,7 +69,7 @@ fn type_op_normalize_ty( fn type_op_normalize_predicate( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Normalize<'tcx, Predicate<'tcx>>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() .enter_canonical_trait_query(&canonicalized, type_op_normalize) @@ -76,7 +77,7 @@ fn type_op_normalize_predicate( fn type_op_normalize_fn_sig( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Normalize<'tcx, FnSig<'tcx>>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() .enter_canonical_trait_query(&canonicalized, type_op_normalize) @@ -84,7 +85,7 @@ fn type_op_normalize_fn_sig( fn type_op_normalize_poly_fn_sig( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Normalize<'tcx, PolyFnSig<'tcx>>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result>>>, NoSolution> { tcx.infer_ctxt() .enter_canonical_trait_query(&canonicalized, type_op_normalize) @@ -92,16 +93,11 @@ fn type_op_normalize_poly_fn_sig( fn type_op_subtype<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, Subtype<'tcx>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Subtype<'tcx>>>, ) -> Result>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query( - &canonicalized, - |infcx, - Subtype { - param_env, - sub, - sup, - }| { + &canonicalized, |infcx, key| { + let (param_env, Subtype { sub, sup }) = key.into_parts(); Ok(infcx .at(&ObligationCause::dummy(), param_env) .sup(sup, sub)?) @@ -111,14 +107,11 @@ fn type_op_subtype<'tcx>( fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, - canonicalized: Canonical<'tcx, ProvePredicate<'tcx>>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, ) -> Result>>, NoSolution> { tcx.infer_ctxt() .enter_canonical_trait_query(&canonicalized, |_infcx, key| { - let ProvePredicate { - param_env, - predicate, - } = key; + let (param_env, ProvePredicate { predicate }) = key.into_parts(); Ok(InferOk { value: (), obligations: vec![Obligation::new( From 5fd3b2628087f254de51074bede5f17765dc1373 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 15:39:20 -0400 Subject: [PATCH 69/72] rename `prequery` to `try_fast_path` --- src/librustc/traits/query/type_op/eq.rs | 2 +- src/librustc/traits/query/type_op/mod.rs | 4 ++-- src/librustc/traits/query/type_op/normalize.rs | 2 +- src/librustc/traits/query/type_op/outlives.rs | 2 +- src/librustc/traits/query/type_op/prove_predicate.rs | 2 +- src/librustc/traits/query/type_op/subtype.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 7c35f448a312e..799e52c6b1921 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -27,7 +27,7 @@ impl<'tcx> Eq<'tcx> { impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { type QueryResult = (); - fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Eq<'tcx>>) -> Option { + fn try_fast_path(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Eq<'tcx>>) -> Option { if key.value.a == key.value.b { Some(()) } else { diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 191fc1aabb17f..8a350d8570d34 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -46,7 +46,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: /// Either converts `self` directly into a `QueryResult` (for /// simple cases) or into a `QueryKey` (for more complex cases /// where we actually have work to do). - fn prequery( + fn try_fast_path( tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option; @@ -83,7 +83,7 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: infcx: &InferCtxt<'_, 'gcx, 'tcx>, output_query_region_constraints: &mut Vec>, ) -> Fallible { - if let Some(result) = QueryTypeOp::prequery(infcx.tcx, &query_key) { + if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { return Ok(result); } diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index 3491b0b4b4ece..0c393fa4ca80f 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -34,7 +34,7 @@ where { type QueryResult = T; - fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { + fn try_fast_path(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { if !key.value.value.has_projections() { Some(key.value.value) } else { diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index bfb476055c825..e41ae7a72f9c2 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -31,7 +31,7 @@ where { type QueryResult = DropckOutlivesResult<'tcx>; - fn prequery( + fn try_fast_path( tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option { diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index dfe640f7431fc..4bb7a8f8d52dc 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -28,7 +28,7 @@ impl<'tcx> ProvePredicate<'tcx> { impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { type QueryResult = (); - fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, _key: &ParamEnvAnd<'tcx, Self>) -> Option { + fn try_fast_path(_tcx: TyCtxt<'_, 'gcx, 'tcx>, _key: &ParamEnvAnd<'tcx, Self>) -> Option { None } diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index db535e3dd13db..dc41bb1d6ab69 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -30,7 +30,7 @@ impl<'tcx> Subtype<'tcx> { impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { type QueryResult = (); - fn prequery(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { + fn try_fast_path(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { if key.value.sub == key.value.sup { Some(()) } else { From f0fdce31d962c3ca4f04f1729ec63af7781ed79f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 15:43:23 -0400 Subject: [PATCH 70/72] update comments --- src/librustc/traits/query/type_op/mod.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 8a350d8570d34..3dfa66cd41a4f 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -26,6 +26,9 @@ pub mod prove_predicate; use self::prove_predicate::ProvePredicate; pub mod subtype; +/// "Type ops" are used in NLL to perform some particular action and +/// extract out the resulting region constraints (or an error if it +/// cannot be completed). pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; @@ -38,14 +41,23 @@ pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { ) -> Fallible<(Self::Output, Option>>>)>; } +/// "Query type ops" are type ops that are implemented using a +/// [canonical query][c]. The `Self` type here contains the kernel of +/// information needed to do the operation -- `TypeOp` is actually +/// implemented for `ParamEnvAnd`, since we always need to bring +/// along a parameter environment as well. For query type-ops, we will +/// first canonicalize the key and then invoke the query on the tcx, +/// which produces the resulting query region constraints. +/// +/// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + Lift<'gcx> { type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; - /// Either converts `self` directly into a `QueryResult` (for - /// simple cases) or into a `QueryKey` (for more complex cases - /// where we actually have work to do). + /// Give query the option for a simple fast path that never + /// actually hits the tcx cache lookup etc. Return `Some(r)` with + /// a final result or `None` to do the full path. fn try_fast_path( tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Self>, From 9b1d2229ff7f408b923c26943d8492a71ea91740 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 16:04:32 -0400 Subject: [PATCH 71/72] change the `enter_canonical_trait_query` method to give a fulfill cx --- src/librustc/infer/canonical/query_result.rs | 5 +- src/librustc/infer/mod.rs | 15 +++++- src/librustc/infer/outlives/bounds.rs | 2 +- src/librustc/traits/engine.rs | 48 +++++++++++-------- src/librustc/traits/fulfill.rs | 12 +---- src/librustc/traits/mod.rs | 2 +- src/librustc/traits/query/type_op/custom.rs | 2 +- src/librustc_traits/dropck_outlives.rs | 2 +- .../normalize_projection_ty.rs | 12 ++--- src/librustc_traits/type_op.rs | 44 +++++++++-------- src/librustc_typeck/check/dropck.rs | 2 +- src/librustc_typeck/lib.rs | 2 +- 12 files changed, 79 insertions(+), 69 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index a4e33b7d86851..24f70eb87575d 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -56,7 +56,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { pub fn enter_canonical_trait_query( &'tcx mut self, canonical_key: &Canonical<'tcx, K>, - op: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, K) -> Fallible>, + op: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut FulfillmentContext<'tcx>, K) -> Fallible, ) -> Fallible> where K: TypeFoldable<'tcx>, @@ -65,9 +65,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { self.enter(|ref infcx| { let (key, canonical_inference_vars) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_key); - let InferOk { value, obligations } = op(infcx, key)?; let fulfill_cx = &mut FulfillmentContext::new(); - fulfill_cx.register_predicate_obligations(infcx, obligations); + let value = op(infcx, fulfill_cx, key)?; infcx.make_canonicalized_query_result(canonical_inference_vars, value, fulfill_cx) }) } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 6b31f869ef9b3..5b5ae6473f840 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -27,7 +27,7 @@ use ty::{self, Ty, TyCtxt, GenericParamDefKind}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use ty::fold::TypeFoldable; use ty::relate::RelateResult; -use traits::{self, ObligationCause, PredicateObligations}; +use traits::{self, ObligationCause, PredicateObligations, TraitEngine}; use rustc_data_structures::unify as ut; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::collections::BTreeMap; @@ -485,6 +485,19 @@ impl<'tcx, T> InferOk<'tcx, T> { pub fn unit(self) -> InferOk<'tcx, ()> { InferOk { value: (), obligations: self.obligations } } + + /// Extract `value`, registering any obligations into `fulfill_cx` + pub fn into_value_registering_obligations( + self, + infcx: &InferCtxt<'_, '_, 'tcx>, + fulfill_cx: &mut impl TraitEngine<'tcx>, + ) -> T { + let InferOk { value, obligations } = self; + for obligation in obligations { + fulfill_cx.register_predicate_obligation(infcx, obligation); + } + value + } } impl<'tcx> InferOk<'tcx, ()> { diff --git a/src/librustc/infer/outlives/bounds.rs b/src/librustc/infer/outlives/bounds.rs index 4bc64acc76306..57abdd18d353c 100644 --- a/src/librustc/infer/outlives/bounds.rs +++ b/src/librustc/infer/outlives/bounds.rs @@ -11,7 +11,7 @@ use infer::InferCtxt; use syntax::ast; use syntax::codemap::Span; -use traits::{FulfillmentContext, TraitEngine}; +use traits::{FulfillmentContext, TraitEngine, TraitEngineExt}; use ty::{self, Ty, TypeFoldable}; use ty::outlives::Component; use ty::wf; diff --git a/src/librustc/traits/engine.rs b/src/librustc/traits/engine.rs index 40d54885619fa..acbf5392cf54c 100644 --- a/src/librustc/traits/engine.rs +++ b/src/librustc/traits/engine.rs @@ -16,56 +16,64 @@ use super::{FulfillmentContext, FulfillmentError}; use super::{ObligationCause, PredicateObligation}; pub trait TraitEngine<'tcx>: 'tcx { - fn normalize_projection_type<'a, 'gcx>( + fn normalize_projection_type( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, ) -> Ty<'tcx>; - fn register_bound<'a, 'gcx>( + fn register_bound( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, def_id: DefId, cause: ObligationCause<'tcx>, ); - fn register_predicate_obligation<'a, 'gcx>( + fn register_predicate_obligation( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, obligation: PredicateObligation<'tcx>, ); - fn select_all_or_error<'a, 'gcx>( + fn select_all_or_error( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, ) -> Result<(), Vec>>; - fn select_where_possible<'a, 'gcx>( + fn select_where_possible( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, ) -> Result<(), Vec>>; fn pending_obligations(&self) -> Vec>; } -impl<'a, 'gcx, 'tcx> dyn TraitEngine<'tcx> { - pub fn new(_tcx: TyCtxt<'_, '_, 'tcx>) -> Box { - Box::new(FulfillmentContext::new()) - } +pub trait TraitEngineExt<'tcx> { + fn register_predicate_obligations( + &mut self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + obligations: impl IntoIterator>, + ); +} - pub fn register_predicate_obligations( +impl> TraitEngineExt<'tcx> for T { + fn register_predicate_obligations( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - obligations: I, - ) where - I: IntoIterator>, - { + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + obligations: impl IntoIterator>, + ) { for obligation in obligations { self.register_predicate_obligation(infcx, obligation); } } } + +impl dyn TraitEngine<'tcx> { + pub fn new(_tcx: TyCtxt<'_, '_, 'tcx>) -> Box { + Box::new(FulfillmentContext::new()) + } +} diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 04396d73df6a2..b3f56d4de6534 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -21,7 +21,7 @@ use middle::const_val::{ConstEvalErr, ErrKind}; use super::CodeAmbiguity; use super::CodeProjectionError; use super::CodeSelectionError; -use super::engine::TraitEngine; +use super::engine::{TraitEngine, TraitEngineExt}; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation, Obligation}; use super::project; @@ -86,16 +86,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { } } - pub fn register_predicate_obligations(&mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - obligations: I) - where I: IntoIterator> - { - for obligation in obligations { - self.register_predicate_obligation(infcx, obligation); - } - } - /// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it /// only attempts to select obligations that haven't been seen before. fn select(&mut self, selcx: &mut SelectionContext<'a, 'gcx, 'tcx>) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index e9e2695fa5c7f..c7e55fa574f94 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -47,7 +47,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::{OverlapError, specialization_graph, translate_substs}; pub use self::specialize::{SpecializesCache, find_associated_item}; -pub use self::engine::TraitEngine; +pub use self::engine::{TraitEngine, TraitEngineExt}; pub use self::util::elaborate_predicates; pub use self::util::supertraits; pub use self::util::Supertraits; diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 90ea276a09449..3d10ce805853d 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -16,7 +16,7 @@ use infer::canonical::query_result; use infer::canonical::QueryRegionConstraint; use std::rc::Rc; use syntax::codemap::DUMMY_SP; -use traits::{ObligationCause, TraitEngine}; +use traits::{ObligationCause, TraitEngine, TraitEngineExt}; pub struct CustomTypeOp { closure: F, diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index eddfee95c8f37..5f9060b362346 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -12,7 +12,7 @@ use rustc::hir::def_id::DefId; use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::traits::query::dropck_outlives::{DropckOutlivesResult, DtorckConstraint}; use rustc::traits::query::{CanonicalTyGoal, NoSolution}; -use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; +use rustc::traits::{FulfillmentContext, Normalized, ObligationCause, TraitEngineExt}; use rustc::ty::query::Providers; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index 1a8899ad8e03f..1c0f677fbf3cb 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -9,9 +9,8 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::infer::InferOk; use rustc::traits::query::{normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution}; -use rustc::traits::{self, ObligationCause, SelectionContext}; +use rustc::traits::{self, ObligationCause, SelectionContext, TraitEngineExt}; use rustc::ty::query::Providers; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; @@ -39,6 +38,7 @@ fn normalize_projection_ty<'tcx>( tcx.infer_ctxt().enter_canonical_trait_query( &goal, |infcx, + fulfill_cx, ParamEnvAnd { param_env, value: goal, @@ -54,11 +54,9 @@ fn normalize_projection_ty<'tcx>( 0, &mut obligations, ); - Ok(InferOk { - value: NormalizationResult { - normalized_ty: answer, - }, - obligations, + fulfill_cx.register_predicate_obligations(infcx, obligations); + Ok(NormalizationResult { + normalized_ty: answer, }) }, ) diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index 9940608222230..ca746722f5810 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -9,15 +9,15 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::infer::{InferCtxt, InferOk}; +use rustc::infer::InferCtxt; use rustc::traits::query::type_op::eq::Eq; use rustc::traits::query::type_op::normalize::Normalize; use rustc::traits::query::type_op::prove_predicate::ProvePredicate; use rustc::traits::query::type_op::subtype::Subtype; use rustc::traits::query::{Fallible, NoSolution}; -use rustc::traits::{Obligation, Normalized, ObligationCause}; +use rustc::traits::{FulfillmentContext, Normalized, Obligation, ObligationCause, TraitEngine, TraitEngineExt}; use rustc::ty::query::Providers; -use rustc::ty::{ParamEnvAnd, FnSig, Lift, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{FnSig, Lift, ParamEnvAnd, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::Lrc; use std::fmt; @@ -39,16 +39,20 @@ fn type_op_eq<'tcx>( canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Eq<'tcx>>>, ) -> Result>>, NoSolution> { tcx.infer_ctxt() - .enter_canonical_trait_query(&canonicalized, |infcx, key| { + .enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { let (param_env, Eq { a, b }) = key.into_parts(); - Ok(infcx.at(&ObligationCause::dummy(), param_env).eq(a, b)?) + Ok(infcx + .at(&ObligationCause::dummy(), param_env) + .eq(a, b)? + .into_value_registering_obligations(infcx, fulfill_cx)) }) } fn type_op_normalize( infcx: &InferCtxt<'_, 'gcx, 'tcx>, + fulfill_cx: &mut FulfillmentContext<'tcx>, key: ParamEnvAnd<'tcx, Normalize>, -) -> Fallible> +) -> Fallible where T: fmt::Debug + TypeFoldable<'tcx> + Lift<'gcx>, { @@ -56,7 +60,8 @@ where let Normalized { value, obligations } = infcx .at(&ObligationCause::dummy(), param_env) .normalize(&value)?; - Ok(InferOk { value, obligations }) // ugh we should merge these two structs + fulfill_cx.register_predicate_obligations(infcx, obligations); + Ok(value) } fn type_op_normalize_ty( @@ -95,14 +100,14 @@ fn type_op_subtype<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Subtype<'tcx>>>, ) -> Result>>, NoSolution> { - tcx.infer_ctxt().enter_canonical_trait_query( - &canonicalized, |infcx, key| { + tcx.infer_ctxt() + .enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { let (param_env, Subtype { sub, sup }) = key.into_parts(); Ok(infcx .at(&ObligationCause::dummy(), param_env) - .sup(sup, sub)?) - }, - ) + .sup(sup, sub)? + .into_value_registering_obligations(infcx, fulfill_cx)) + }) } fn type_op_prove_predicate<'tcx>( @@ -110,15 +115,12 @@ fn type_op_prove_predicate<'tcx>( canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, ) -> Result>>, NoSolution> { tcx.infer_ctxt() - .enter_canonical_trait_query(&canonicalized, |_infcx, key| { + .enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { let (param_env, ProvePredicate { predicate }) = key.into_parts(); - Ok(InferOk { - value: (), - obligations: vec![Obligation::new( - ObligationCause::dummy(), - param_env, - predicate, - )], - }) + fulfill_cx.register_predicate_obligation( + infcx, + Obligation::new(ObligationCause::dummy(), param_env, predicate), + ); + Ok(()) }) } diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index b8aa39a202bee..f9166851f6fcc 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -16,7 +16,7 @@ use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::traits::{ObligationCause, TraitEngine}; +use rustc::traits::{ObligationCause, TraitEngine, TraitEngineExt}; use util::common::ErrorReported; use syntax::ast; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index dcc5fa53d2f42..b18e5ca54ff47 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -108,7 +108,7 @@ use rustc::infer::InferOk; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::query::Providers; -use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine}; +use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt}; use session::{CompileIncomplete, config}; use util::common::time; From 1523de34a2267b624f1b920c1b4496568bd8c16b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 27 Jun 2018 16:17:49 -0400 Subject: [PATCH 72/72] rustfmt various files --- src/librustc/infer/canonical/query_result.rs | 5 +++-- src/librustc/traits/query/type_op/eq.rs | 5 ++++- src/librustc/traits/query/type_op/prove_predicate.rs | 9 +++++---- src/librustc_traits/type_op.rs | 3 ++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index 24f70eb87575d..b8b13e03afaf9 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -56,7 +56,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { pub fn enter_canonical_trait_query( &'tcx mut self, canonical_key: &Canonical<'tcx, K>, - op: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut FulfillmentContext<'tcx>, K) -> Fallible, + operation: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut FulfillmentContext<'tcx>, K) + -> Fallible, ) -> Fallible> where K: TypeFoldable<'tcx>, @@ -66,7 +67,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { let (key, canonical_inference_vars) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_key); let fulfill_cx = &mut FulfillmentContext::new(); - let value = op(infcx, fulfill_cx, key)?; + let value = operation(infcx, fulfill_cx, key)?; infcx.make_canonicalized_query_result(canonical_inference_vars, value, fulfill_cx) }) } diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index 799e52c6b1921..52a087cbc8069 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -27,7 +27,10 @@ impl<'tcx> Eq<'tcx> { impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { type QueryResult = (); - fn try_fast_path(_tcx: TyCtxt<'_, 'gcx, 'tcx>, key: &ParamEnvAnd<'tcx, Eq<'tcx>>) -> Option { + fn try_fast_path( + _tcx: TyCtxt<'_, 'gcx, 'tcx>, + key: &ParamEnvAnd<'tcx, Eq<'tcx>>, + ) -> Option { if key.value.a == key.value.b { Some(()) } else { diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 4bb7a8f8d52dc..33dc3210f0881 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -19,16 +19,17 @@ pub struct ProvePredicate<'tcx> { impl<'tcx> ProvePredicate<'tcx> { pub fn new(predicate: Predicate<'tcx>) -> Self { - ProvePredicate { - predicate, - } + ProvePredicate { predicate } } } impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { type QueryResult = (); - fn try_fast_path(_tcx: TyCtxt<'_, 'gcx, 'tcx>, _key: &ParamEnvAnd<'tcx, Self>) -> Option { + fn try_fast_path( + _tcx: TyCtxt<'_, 'gcx, 'tcx>, + _key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { None } diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index ca746722f5810..8fe4290528e74 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -15,7 +15,8 @@ use rustc::traits::query::type_op::normalize::Normalize; use rustc::traits::query::type_op::prove_predicate::ProvePredicate; use rustc::traits::query::type_op::subtype::Subtype; use rustc::traits::query::{Fallible, NoSolution}; -use rustc::traits::{FulfillmentContext, Normalized, Obligation, ObligationCause, TraitEngine, TraitEngineExt}; +use rustc::traits::{FulfillmentContext, Normalized, Obligation, ObligationCause, TraitEngine, + TraitEngineExt}; use rustc::ty::query::Providers; use rustc::ty::{FnSig, Lift, ParamEnvAnd, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::Lrc;