Skip to content

Commit 89b065d

Browse files
authored
Rollup merge of rust-lang#67914 - Aaron1011:fix/const-prop-impossible, r=matthewjasper,oli-obk
Don't run const propagation on items with inconsistent bounds Fixes rust-lang#67696 Using `#![feature(trivial_bounds)]`, it's possible to write functions with unsatisfiable 'where' clauses, making them uncallable. However, the user can act as if these 'where' clauses are true inside the body of the function, leading to code that would normally be impossible to write. Since const propgation can run even without any user-written calls to a function, we need to explcitly check for these uncallable functions.
2 parents e800fe1 + e390b91 commit 89b065d

File tree

8 files changed

+115
-14
lines changed

8 files changed

+115
-14
lines changed

src/librustc/mir/mono.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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;
45
use crate::ty::print::obsolete::DefPathBasedNames;
56
use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt};
67
use rustc_data_structures::base_n;
@@ -167,7 +168,9 @@ impl<'tcx> MonoItem<'tcx> {
167168
MonoItem::GlobalAsm(..) => return true,
168169
};
169170

170-
tcx.substitute_normalize_and_test_predicates((def_id, &substs))
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))
171174
}
172175

173176
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
@@ -1148,11 +1148,11 @@ rustc_queries! {
11481148
desc { "normalizing `{:?}`", goal }
11491149
}
11501150

1151-
query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool {
1151+
query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>, traits::TraitQueryMode)) -> bool {
11521152
no_force
11531153
desc { |tcx|
1154-
"testing substituted normalized predicates:`{}`",
1155-
tcx.def_path_str(key.0)
1154+
"testing substituted normalized predicates in mode {:?}:`{}`",
1155+
key.2, tcx.def_path_str(key.0)
11561156
}
11571157
}
11581158

src/librustc/traits/fulfill.rs

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

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

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

7781
impl<'a, 'tcx> FulfillmentContext<'tcx> {
78-
/// Creates a new fulfillment context.
82+
/// Creates a new fulfillment context with `TraitQueryMode::Standard`
83+
/// You almost always want to use this instead of `with_query_mode`
7984
pub fn new() -> FulfillmentContext<'tcx> {
8085
FulfillmentContext {
8186
predicates: ObligationForest::new(),
8287
register_region_obligations: true,
8388
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,
84102
}
85103
}
86104

@@ -89,6 +107,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
89107
predicates: ObligationForest::new(),
90108
register_region_obligations: true,
91109
usable_in_snapshot: true,
110+
query_mode: TraitQueryMode::Standard,
92111
}
93112
}
94113

@@ -97,6 +116,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
97116
predicates: ObligationForest::new(),
98117
register_region_obligations: false,
99118
usable_in_snapshot: false,
119+
query_mode: TraitQueryMode::Standard,
100120
}
101121
}
102122

@@ -217,7 +237,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
217237
&mut self,
218238
infcx: &InferCtxt<'_, 'tcx>,
219239
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
220-
let mut selcx = SelectionContext::new(infcx);
240+
let mut selcx = SelectionContext::with_query_mode(infcx, self.query_mode);
221241
self.select(&mut selcx)
222242
}
223243

src/librustc/traits/mod.rs

Lines changed: 11 additions & 7 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)]
98+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable)]
9999
pub enum TraitQueryMode {
100100
// Standard/un-canonicalized queries get accurate
101101
// spans etc. passed in and hence can do reasonable
@@ -1017,13 +1017,14 @@ where
10171017
fn normalize_and_test_predicates<'tcx>(
10181018
tcx: TyCtxt<'tcx>,
10191019
predicates: Vec<ty::Predicate<'tcx>>,
1020+
mode: TraitQueryMode,
10201021
) -> bool {
1021-
debug!("normalize_and_test_predicates(predicates={:?})", predicates);
1022+
debug!("normalize_and_test_predicates(predicates={:?}, mode={:?})", predicates, mode);
10221023

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

10441045
fn substitute_normalize_and_test_predicates<'tcx>(
10451046
tcx: TyCtxt<'tcx>,
1046-
key: (DefId, SubstsRef<'tcx>),
1047+
key: (DefId, SubstsRef<'tcx>, TraitQueryMode),
10471048
) -> bool {
10481049
debug!("substitute_normalize_and_test_predicates(key={:?})", key);
10491050

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

10531054
debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}", key, result);
10541055
result
@@ -1101,7 +1102,10 @@ fn vtable_methods<'tcx>(
11011102
// Note that this method could then never be called, so we
11021103
// do not want to try and codegen it, in that case (see #23435).
11031104
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
1104-
if !normalize_and_test_predicates(tcx, predicates.predicates) {
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+
{
11051109
debug!("vtable_methods: predicates do not hold");
11061110
return None;
11071111
}

src/librustc/ty/query/keys.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ 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+
128137
impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
129138
fn query_crate(&self) -> CrateNum {
130139
self.1.def_id().krate

src/librustc_mir/transform/const_prop.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc::mir::{
1414
SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
1515
UnOp, RETURN_PLACE,
1616
};
17+
use rustc::traits::TraitQueryMode;
1718
use rustc::ty::layout::{
1819
HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
1920
};
@@ -74,6 +75,46 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
7475
return;
7576
}
7677

78+
// Check if it's even possible to satisfy the 'where' clauses
79+
// for this item.
80+
// This branch will never be taken for any normal function.
81+
// However, it's possible to `#!feature(trivial_bounds)]` to write
82+
// a function with impossible to satisfy clauses, e.g.:
83+
// `fn foo() where String: Copy {}`
84+
//
85+
// We don't usually need to worry about this kind of case,
86+
// since we would get a compilation error if the user tried
87+
// to call it. However, since we can do const propagation
88+
// even without any calls to the function, we need to make
89+
// sure that it even makes sense to try to evaluate the body.
90+
// If there are unsatisfiable where clauses, then all bets are
91+
// off, and we just give up.
92+
//
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.
101+
//
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+
);
115+
return;
116+
}
117+
77118
trace!("ConstProp starting for {:?}", source.def_id());
78119

79120
let dummy_body = &Body::new(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// check-pass
2+
// compile-flags: --emit=mir,link
3+
// Checks that we don't ICE due to attempting to run const prop
4+
// on a function with unsatisifable 'where' clauses
5+
6+
#![allow(unused)]
7+
8+
trait A {
9+
fn foo(&self) -> Self where Self: Copy;
10+
}
11+
12+
impl A for [fn(&())] {
13+
fn foo(&self) -> Self where Self: Copy { *(&[] as &[_]) }
14+
}
15+
16+
impl A for i32 {
17+
fn foo(&self) -> Self { 3 }
18+
}
19+
20+
fn main() {}

src/test/ui/trivial-bounds/trivial-bounds-inconsistent-associated-functions.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
// run-pass
1+
// check-pass
2+
// compile-flags: --emit=mir,link
3+
// Force mir to be emitted, to ensure that const
4+
// propagation doesn't ICE on a function
5+
// with an 'impossible' body. See issue #67696
26
// Inconsistent bounds with trait implementations
37

48
#![feature(trivial_bounds)]

0 commit comments

Comments
 (0)