From d088d8a2c1bd706c458d36eac941949169514d86 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 16 Jan 2020 18:47:52 -0500 Subject: [PATCH 1/4] Revert previous attempt at detecting unsatisfiable predicates --- src/librustc/mir/mono.rs | 5 +---- src/librustc/query/mod.rs | 6 +++--- src/librustc/traits/fulfill.rs | 24 ++---------------------- src/librustc/traits/mod.rs | 20 ++++++++------------ src/librustc/ty/query/keys.rs | 9 --------- 5 files changed, 14 insertions(+), 50 deletions(-) diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index e7a4c5b592105..51ce575e51f3b 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -1,7 +1,6 @@ use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; use crate::ich::{Fingerprint, NodeIdHashingMode, StableHashingContext}; use crate::session::config::OptLevel; -use crate::traits::TraitQueryMode; use crate::ty::print::obsolete::DefPathBasedNames; use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt}; use rustc_data_structures::base_n; @@ -168,9 +167,7 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::GlobalAsm(..) => return true, }; - // We shouldn't encounter any overflow here, so we use TraitQueryMode::Standard\ - // to report an error if overflow somehow occurs. - tcx.substitute_normalize_and_test_predicates((def_id, &substs, TraitQueryMode::Standard)) + tcx.substitute_normalize_and_test_predicates((def_id, &substs)) } pub fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String { diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index a20e011b91a75..f4c262fbac1d4 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -1148,11 +1148,11 @@ rustc_queries! { desc { "normalizing `{:?}`", goal } } - query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>, traits::TraitQueryMode)) -> bool { + query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { no_force desc { |tcx| - "testing substituted normalized predicates in mode {:?}:`{}`", - key.2, tcx.def_path_str(key.0) + "testing substituted normalized predicates:`{}`", + tcx.def_path_str(key.0) } } diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 9e5abc80822c7..46ece6fc40593 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -16,7 +16,6 @@ use super::CodeSelectionError; use super::{ConstEvalFailure, Unimplemented}; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; -use crate::traits::TraitQueryMode; impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { type Predicate = ty::Predicate<'tcx>; @@ -63,9 +62,6 @@ pub struct FulfillmentContext<'tcx> { // a snapshot (they don't *straddle* a snapshot, so there // is no trouble there). usable_in_snapshot: bool, - - // The `TraitQueryMode` used when constructing a `SelectionContext` - query_mode: TraitQueryMode, } #[derive(Clone, Debug)] @@ -79,26 +75,12 @@ pub struct PendingPredicateObligation<'tcx> { static_assert_size!(PendingPredicateObligation<'_>, 136); impl<'a, 'tcx> FulfillmentContext<'tcx> { - /// Creates a new fulfillment context with `TraitQueryMode::Standard` - /// You almost always want to use this instead of `with_query_mode` + /// Creates a new fulfillment context. pub fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), register_region_obligations: true, usable_in_snapshot: false, - query_mode: TraitQueryMode::Standard, - } - } - - /// Creates a new fulfillment context with the specified query mode. - /// This should only be used when you want to ignore overflow, - /// rather than reporting it as an error. - pub fn with_query_mode(query_mode: TraitQueryMode) -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: true, - usable_in_snapshot: false, - query_mode, } } @@ -107,7 +89,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { predicates: ObligationForest::new(), register_region_obligations: true, usable_in_snapshot: true, - query_mode: TraitQueryMode::Standard, } } @@ -116,7 +97,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { predicates: ObligationForest::new(), register_region_obligations: false, usable_in_snapshot: false, - query_mode: TraitQueryMode::Standard, } } @@ -237,7 +217,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { &mut self, infcx: &InferCtxt<'_, 'tcx>, ) -> Result<(), Vec>> { - let mut selcx = SelectionContext::with_query_mode(infcx, self.query_mode); + let mut selcx = SelectionContext::new(infcx); self.select(&mut selcx) } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 31de5409fc8be..7509c2251f083 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -95,7 +95,7 @@ pub enum IntercrateMode { } /// The mode that trait queries run in. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum TraitQueryMode { // Standard/un-canonicalized queries get accurate // spans etc. passed in and hence can do reasonable @@ -1014,17 +1014,16 @@ where /// environment. If this returns false, then either normalize /// encountered an error or one of the predicates did not hold. Used /// when creating vtables to check for unsatisfiable methods. -fn normalize_and_test_predicates<'tcx>( +pub fn normalize_and_test_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: Vec>, - mode: TraitQueryMode, ) -> bool { - debug!("normalize_and_test_predicates(predicates={:?}, mode={:?})", predicates, mode); + debug!("normalize_and_test_predicates(predicates={:?})", predicates); let result = tcx.infer_ctxt().enter(|infcx| { let param_env = ty::ParamEnv::reveal_all(); - let mut selcx = SelectionContext::with_query_mode(&infcx, mode); - let mut fulfill_cx = FulfillmentContext::with_query_mode(mode); + let mut selcx = SelectionContext::new(&infcx); + let mut fulfill_cx = FulfillmentContext::new(); let cause = ObligationCause::dummy(); let Normalized { value: predicates, obligations } = normalize(&mut selcx, param_env, cause.clone(), &predicates); @@ -1044,12 +1043,12 @@ fn normalize_and_test_predicates<'tcx>( fn substitute_normalize_and_test_predicates<'tcx>( tcx: TyCtxt<'tcx>, - key: (DefId, SubstsRef<'tcx>, TraitQueryMode), + key: (DefId, SubstsRef<'tcx>), ) -> bool { debug!("substitute_normalize_and_test_predicates(key={:?})", key); let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; - let result = normalize_and_test_predicates(tcx, predicates, key.2); + let result = normalize_and_test_predicates(tcx, predicates); debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}", key, result); result @@ -1102,10 +1101,7 @@ fn vtable_methods<'tcx>( // Note that this method could then never be called, so we // do not want to try and codegen it, in that case (see #23435). let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - // We don't expect overflow here, so report an error if it somehow ends - // up happening. - if !normalize_and_test_predicates(tcx, predicates.predicates, TraitQueryMode::Standard) - { + if !normalize_and_test_predicates(tcx, predicates.predicates) { debug!("vtable_methods: predicates do not hold"); return None; } diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs index 3fb3720a5638a..cbf335ad607ef 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc/ty/query/keys.rs @@ -125,15 +125,6 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) { } } -impl<'tcx> Key for (DefId, SubstsRef<'tcx>, traits::TraitQueryMode) { - fn query_crate(&self) -> CrateNum { - self.0.krate - } - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.0.default_span(tcx) - } -} - impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) { fn query_crate(&self) -> CrateNum { self.1.def_id().krate From 171fe82efc2ab420e3fe2e9ad5a44db093a751f1 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 16 Jan 2020 18:53:51 -0500 Subject: [PATCH 2/4] Filter and test predicates using `normalize_and_test_predicates` for const-prop Fixes #68264 Previously, I attempted to use `substitute_normalize_and_test_predicates` to detect unsatisfiable bounds. Unfortunately, since const-prop runs in a generic environment (we don't have any of the function's generic parameters substituted), this could lead to cycle errors when attempting to normalize predicates. This check is replaced with a more precise check. We now only call `normalize_and_test_predicates` on predicates that have the possibility of being proved unsatisfiable - that is, predicates that don't depend on anything local to the function (e.g. generic parameters). This ensures that we don't hit cycle errors when we normalize said predicates, while still ensuring that we detect unsatisfiable predicates. --- src/librustc_mir/transform/const_prop.rs | 44 ++++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 9d5dbe3564ee0..badca10569404 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -14,7 +14,7 @@ use rustc::mir::{ SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, }; -use rustc::traits::TraitQueryMode; +use rustc::traits; use rustc::ty::layout::{ HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout, }; @@ -90,28 +90,28 @@ impl<'tcx> MirPass<'tcx> for ConstProp { // If there are unsatisfiable where clauses, then all bets are // off, and we just give up. // - // Note that we use TraitQueryMode::Canonical here, which causes - // us to treat overflow like any other error. This is because we - // are "speculatively" evaluating this item with the default substs. - // While this usually succeeds, it may fail with tricky impls - // (e.g. the typenum crate). Const-propagation is fundamentally - // "best-effort", and does not affect correctness in any way. - // Therefore, it's perfectly fine to just "give up" if we're - // unable to check the bounds with the default substs. + // We manually filter the predicates, skipping anything that's not + // "global". We are in a potentially generic context + // (e.g. we are evaluating a function without substituging generic + // parameters, so this filtering serves two purposes: // - // False negatives (failing to run const-prop on something when we actually - // could) are fine. However, false positives (running const-prop on - // an item with unsatisfiable bounds) can lead to us generating invalid - // MIR. - if !tcx.substitute_normalize_and_test_predicates(( - source.def_id(), - InternalSubsts::identity_for_item(tcx, source.def_id()), - TraitQueryMode::Canonical, - )) { - trace!( - "ConstProp skipped for item with unsatisfiable predicates: {:?}", - source.def_id() - ); + // 1. We skip evaluating any predicates that we would + // never be able prove are unsatisfiable (e.g. `` + // 2. We avoid trying to normalize predicates involving generic + // parameters (e.g. `::MyItem`). This can confuse + // the normalization code (leading to cycle errors), since + // it's usually never invoked in this way. + let predicates = tcx + .predicates_of(source.def_id()) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }) + .collect(); + if !traits::normalize_and_test_predicates( + tcx, + traits::elaborate_predicates(tcx, predicates).collect(), + ) { + trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", source.def_id()); return; } From c431cd751f6b0257443cd4d25c67ab36a8e9bc12 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 17 Jan 2020 08:13:04 -0500 Subject: [PATCH 3/4] Fix typo Co-Authored-By: Oliver Scherer --- src/librustc_mir/transform/const_prop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index badca10569404..5cbee929019dd 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -92,7 +92,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { // // We manually filter the predicates, skipping anything that's not // "global". We are in a potentially generic context - // (e.g. we are evaluating a function without substituging generic + // (e.g. we are evaluating a function without substituting generic // parameters, so this filtering serves two purposes: // // 1. We skip evaluating any predicates that we would From 3fef3d8b76e70da88366e45a62f98592cf9be76c Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 17 Jan 2020 08:22:40 -0500 Subject: [PATCH 4/4] Add @weiznich's regression test --- src/test/ui/consts/issue-68264-overflow.rs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/test/ui/consts/issue-68264-overflow.rs diff --git a/src/test/ui/consts/issue-68264-overflow.rs b/src/test/ui/consts/issue-68264-overflow.rs new file mode 100644 index 0000000000000..8f21e0648d4c7 --- /dev/null +++ b/src/test/ui/consts/issue-68264-overflow.rs @@ -0,0 +1,43 @@ +// check-pass +// compile-flags: --emit=mir,link +// Regression test for issue #68264 +// Checks that we don't encounter overflow +// when running const-prop on functions with +// complicated bounds +pub trait Query {} + +pub trait AsQuery { + type Query: Query; +} +pub trait Table: AsQuery + Sized {} + +pub trait LimitDsl { + type Output; +} + +pub(crate) trait LoadQuery: RunQueryDsl {} + +impl AsQuery for T { + type Query = Self; +} + +impl LimitDsl for T +where + T: Table, + T::Query: LimitDsl, +{ + type Output = ::Output; +} + +pub(crate) trait RunQueryDsl: Sized { + fn first(self, _conn: &Conn) -> U + where + Self: LimitDsl, + Self::Output: LoadQuery, + { + // Overflow is caused by this function body + unimplemented!() + } +} + +fn main() {}