Skip to content

Commit bff216c

Browse files
authored
Rollup merge of rust-lang#68297 - Aaron1011:fix/new-const-prop-bounds, r=oli-obk
Filter and test predicates using `normalize_and_test_predicates` for const-prop Fixes rust-lang#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. I haven't been able to come up with a minimization of the Diesel issue - however, I've verified that it compiles successfully.
2 parents eff6381 + 3fef3d8 commit bff216c

File tree

7 files changed

+79
-72
lines changed

7 files changed

+79
-72
lines changed

src/librustc/mir/mono.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId};
22
use crate::ich::{Fingerprint, NodeIdHashingMode, StableHashingContext};
33
use crate::session::config::OptLevel;
4-
use crate::traits::TraitQueryMode;
54
use crate::ty::print::obsolete::DefPathBasedNames;
65
use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt};
76
use rustc_data_structures::base_n;
@@ -168,9 +167,7 @@ impl<'tcx> MonoItem<'tcx> {
168167
MonoItem::GlobalAsm(..) => return true,
169168
};
170169

171-
// We shouldn't encounter any overflow here, so we use TraitQueryMode::Standard\
172-
// to report an error if overflow somehow occurs.
173-
tcx.substitute_normalize_and_test_predicates((def_id, &substs, TraitQueryMode::Standard))
170+
tcx.substitute_normalize_and_test_predicates((def_id, &substs))
174171
}
175172

176173
pub fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String {

src/librustc/query/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,11 +1156,11 @@ rustc_queries! {
11561156
desc { "normalizing `{:?}`", goal }
11571157
}
11581158

1159-
query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>, traits::TraitQueryMode)) -> bool {
1159+
query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool {
11601160
no_force
11611161
desc { |tcx|
1162-
"testing substituted normalized predicates in mode {:?}:`{}`",
1163-
key.2, tcx.def_path_str(key.0)
1162+
"testing substituted normalized predicates:`{}`",
1163+
tcx.def_path_str(key.0)
11641164
}
11651165
}
11661166

src/librustc/traits/fulfill.rs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use super::CodeSelectionError;
1616
use super::{ConstEvalFailure, Unimplemented};
1717
use super::{FulfillmentError, FulfillmentErrorCode};
1818
use super::{ObligationCause, PredicateObligation};
19-
use crate::traits::TraitQueryMode;
2019

2120
impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
2221
type Predicate = ty::Predicate<'tcx>;
@@ -63,9 +62,6 @@ pub struct FulfillmentContext<'tcx> {
6362
// a snapshot (they don't *straddle* a snapshot, so there
6463
// is no trouble there).
6564
usable_in_snapshot: bool,
66-
67-
// The `TraitQueryMode` used when constructing a `SelectionContext`
68-
query_mode: TraitQueryMode,
6965
}
7066

7167
#[derive(Clone, Debug)]
@@ -79,26 +75,12 @@ pub struct PendingPredicateObligation<'tcx> {
7975
static_assert_size!(PendingPredicateObligation<'_>, 136);
8076

8177
impl<'a, 'tcx> FulfillmentContext<'tcx> {
82-
/// Creates a new fulfillment context with `TraitQueryMode::Standard`
83-
/// You almost always want to use this instead of `with_query_mode`
78+
/// Creates a new fulfillment context.
8479
pub fn new() -> FulfillmentContext<'tcx> {
8580
FulfillmentContext {
8681
predicates: ObligationForest::new(),
8782
register_region_obligations: true,
8883
usable_in_snapshot: false,
89-
query_mode: TraitQueryMode::Standard,
90-
}
91-
}
92-
93-
/// Creates a new fulfillment context with the specified query mode.
94-
/// This should only be used when you want to ignore overflow,
95-
/// rather than reporting it as an error.
96-
pub fn with_query_mode(query_mode: TraitQueryMode) -> FulfillmentContext<'tcx> {
97-
FulfillmentContext {
98-
predicates: ObligationForest::new(),
99-
register_region_obligations: true,
100-
usable_in_snapshot: false,
101-
query_mode,
10284
}
10385
}
10486

@@ -107,7 +89,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
10789
predicates: ObligationForest::new(),
10890
register_region_obligations: true,
10991
usable_in_snapshot: true,
110-
query_mode: TraitQueryMode::Standard,
11192
}
11293
}
11394

@@ -116,7 +97,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
11697
predicates: ObligationForest::new(),
11798
register_region_obligations: false,
11899
usable_in_snapshot: false,
119-
query_mode: TraitQueryMode::Standard,
120100
}
121101
}
122102

@@ -237,7 +217,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
237217
&mut self,
238218
infcx: &InferCtxt<'_, 'tcx>,
239219
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
240-
let mut selcx = SelectionContext::with_query_mode(infcx, self.query_mode);
220+
let mut selcx = SelectionContext::new(infcx);
241221
self.select(&mut selcx)
242222
}
243223

src/librustc/traits/mod.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub enum IntercrateMode {
9595
}
9696

9797
/// The mode that trait queries run in.
98-
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable)]
98+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9999
pub enum TraitQueryMode {
100100
// Standard/un-canonicalized queries get accurate
101101
// spans etc. passed in and hence can do reasonable
@@ -1014,17 +1014,16 @@ where
10141014
/// environment. If this returns false, then either normalize
10151015
/// encountered an error or one of the predicates did not hold. Used
10161016
/// when creating vtables to check for unsatisfiable methods.
1017-
fn normalize_and_test_predicates<'tcx>(
1017+
pub fn normalize_and_test_predicates<'tcx>(
10181018
tcx: TyCtxt<'tcx>,
10191019
predicates: Vec<ty::Predicate<'tcx>>,
1020-
mode: TraitQueryMode,
10211020
) -> bool {
1022-
debug!("normalize_and_test_predicates(predicates={:?}, mode={:?})", predicates, mode);
1021+
debug!("normalize_and_test_predicates(predicates={:?})", predicates);
10231022

10241023
let result = tcx.infer_ctxt().enter(|infcx| {
10251024
let param_env = ty::ParamEnv::reveal_all();
1026-
let mut selcx = SelectionContext::with_query_mode(&infcx, mode);
1027-
let mut fulfill_cx = FulfillmentContext::with_query_mode(mode);
1025+
let mut selcx = SelectionContext::new(&infcx);
1026+
let mut fulfill_cx = FulfillmentContext::new();
10281027
let cause = ObligationCause::dummy();
10291028
let Normalized { value: predicates, obligations } =
10301029
normalize(&mut selcx, param_env, cause.clone(), &predicates);
@@ -1044,12 +1043,12 @@ fn normalize_and_test_predicates<'tcx>(
10441043

10451044
fn substitute_normalize_and_test_predicates<'tcx>(
10461045
tcx: TyCtxt<'tcx>,
1047-
key: (DefId, SubstsRef<'tcx>, TraitQueryMode),
1046+
key: (DefId, SubstsRef<'tcx>),
10481047
) -> bool {
10491048
debug!("substitute_normalize_and_test_predicates(key={:?})", key);
10501049

10511050
let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
1052-
let result = normalize_and_test_predicates(tcx, predicates, key.2);
1051+
let result = normalize_and_test_predicates(tcx, predicates);
10531052

10541053
debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}", key, result);
10551054
result
@@ -1102,10 +1101,7 @@ fn vtable_methods<'tcx>(
11021101
// Note that this method could then never be called, so we
11031102
// do not want to try and codegen it, in that case (see #23435).
11041103
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
1105-
// We don't expect overflow here, so report an error if it somehow ends
1106-
// up happening.
1107-
if !normalize_and_test_predicates(tcx, predicates.predicates, TraitQueryMode::Standard)
1108-
{
1104+
if !normalize_and_test_predicates(tcx, predicates.predicates) {
11091105
debug!("vtable_methods: predicates do not hold");
11101106
return None;
11111107
}

src/librustc/ty/query/keys.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,6 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
125125
}
126126
}
127127

128-
impl<'tcx> Key for (DefId, SubstsRef<'tcx>, traits::TraitQueryMode) {
129-
fn query_crate(&self) -> CrateNum {
130-
self.0.krate
131-
}
132-
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
133-
self.0.default_span(tcx)
134-
}
135-
}
136-
137128
impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
138129
fn query_crate(&self) -> CrateNum {
139130
self.1.def_id().krate

src/librustc_mir/transform/const_prop.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc::mir::{
1414
SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
1515
UnOp, RETURN_PLACE,
1616
};
17-
use rustc::traits::TraitQueryMode;
17+
use rustc::traits;
1818
use rustc::ty::layout::{
1919
HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
2020
};
@@ -90,28 +90,28 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
9090
// If there are unsatisfiable where clauses, then all bets are
9191
// off, and we just give up.
9292
//
93-
// Note that we use TraitQueryMode::Canonical here, which causes
94-
// us to treat overflow like any other error. This is because we
95-
// are "speculatively" evaluating this item with the default substs.
96-
// While this usually succeeds, it may fail with tricky impls
97-
// (e.g. the typenum crate). Const-propagation is fundamentally
98-
// "best-effort", and does not affect correctness in any way.
99-
// Therefore, it's perfectly fine to just "give up" if we're
100-
// unable to check the bounds with the default substs.
93+
// We manually filter the predicates, skipping anything that's not
94+
// "global". We are in a potentially generic context
95+
// (e.g. we are evaluating a function without substituting generic
96+
// parameters, so this filtering serves two purposes:
10197
//
102-
// False negatives (failing to run const-prop on something when we actually
103-
// could) are fine. However, false positives (running const-prop on
104-
// an item with unsatisfiable bounds) can lead to us generating invalid
105-
// MIR.
106-
if !tcx.substitute_normalize_and_test_predicates((
107-
source.def_id(),
108-
InternalSubsts::identity_for_item(tcx, source.def_id()),
109-
TraitQueryMode::Canonical,
110-
)) {
111-
trace!(
112-
"ConstProp skipped for item with unsatisfiable predicates: {:?}",
113-
source.def_id()
114-
);
98+
// 1. We skip evaluating any predicates that we would
99+
// never be able prove are unsatisfiable (e.g. `<T as Foo>`
100+
// 2. We avoid trying to normalize predicates involving generic
101+
// parameters (e.g. `<T as Foo>::MyItem`). This can confuse
102+
// the normalization code (leading to cycle errors), since
103+
// it's usually never invoked in this way.
104+
let predicates = tcx
105+
.predicates_of(source.def_id())
106+
.predicates
107+
.iter()
108+
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None })
109+
.collect();
110+
if !traits::normalize_and_test_predicates(
111+
tcx,
112+
traits::elaborate_predicates(tcx, predicates).collect(),
113+
) {
114+
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", source.def_id());
115115
return;
116116
}
117117

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// check-pass
2+
// compile-flags: --emit=mir,link
3+
// Regression test for issue #68264
4+
// Checks that we don't encounter overflow
5+
// when running const-prop on functions with
6+
// complicated bounds
7+
pub trait Query {}
8+
9+
pub trait AsQuery {
10+
type Query: Query;
11+
}
12+
pub trait Table: AsQuery + Sized {}
13+
14+
pub trait LimitDsl {
15+
type Output;
16+
}
17+
18+
pub(crate) trait LoadQuery<Conn, U>: RunQueryDsl<Conn> {}
19+
20+
impl<T: Query> AsQuery for T {
21+
type Query = Self;
22+
}
23+
24+
impl<T> LimitDsl for T
25+
where
26+
T: Table,
27+
T::Query: LimitDsl,
28+
{
29+
type Output = <T::Query as LimitDsl>::Output;
30+
}
31+
32+
pub(crate) trait RunQueryDsl<Conn>: Sized {
33+
fn first<U>(self, _conn: &Conn) -> U
34+
where
35+
Self: LimitDsl,
36+
Self::Output: LoadQuery<Conn, U>,
37+
{
38+
// Overflow is caused by this function body
39+
unimplemented!()
40+
}
41+
}
42+
43+
fn main() {}

0 commit comments

Comments
 (0)