Skip to content

Commit c318691

Browse files
committed
Auto merge of #52626 - brunocodutra:issue-52475, r=oli-obk
Fix issue #52475: Make loop detector only consider reachable memory As [suggested](#51702 (comment)) by @oli-obk `alloc_id`s should be ignored by traversing all `Allocation`s in interpreter memory at a given moment in time, beginning by `ByRef` locals in the stack. - [x] Generalize the implementation of `Hash` for `EvalSnapshot` to traverse `Allocation`s - [x] Generalize the implementation of `PartialEq` for `EvalSnapshot` to traverse `Allocation`s - [x] Commit regression tests Fixes #52626 Fixes #52849
2 parents 35a5541 + 05cdf8d commit c318691

File tree

16 files changed

+606
-193
lines changed

16 files changed

+606
-193
lines changed

src/librustc/ich/hcx.rs

-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use hir::map::definitions::Definitions;
1515
use ich::{self, CachingSourceMapView, Fingerprint};
1616
use middle::cstore::CrateStore;
1717
use ty::{TyCtxt, fast_reject};
18-
use mir::interpret::AllocId;
1918
use session::Session;
2019

2120
use std::cmp::Ord;
@@ -60,8 +59,6 @@ pub struct StableHashingContext<'a> {
6059
// CachingSourceMapView, so we initialize it lazily.
6160
raw_source_map: &'a SourceMap,
6261
caching_source_map: Option<CachingSourceMapView<'a>>,
63-
64-
pub(super) alloc_id_recursion_tracker: FxHashSet<AllocId>,
6562
}
6663

6764
#[derive(PartialEq, Eq, Clone, Copy)]
@@ -105,7 +102,6 @@ impl<'a> StableHashingContext<'a> {
105102
hash_spans: hash_spans_initial,
106103
hash_bodies: true,
107104
node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
108-
alloc_id_recursion_tracker: Default::default(),
109105
}
110106
}
111107

src/librustc/ich/impls_ty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
412412
ty::tls::with_opt(|tcx| {
413413
trace!("hashing {:?}", *self);
414414
let tcx = tcx.expect("can't hash AllocIds during hir lowering");
415-
let alloc_kind = tcx.alloc_map.lock().get(*self).expect("no value for AllocId");
415+
let alloc_kind = tcx.alloc_map.lock().get(*self);
416416
alloc_kind.hash_stable(hcx, hasher);
417417
});
418418
}

src/librustc/mir/interpret/mod.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,14 @@ pub trait PointerArithmetic: layout::HasDataLayout {
133133
impl<T: layout::HasDataLayout> PointerArithmetic for T {}
134134

135135

136+
/// Pointer is generic over the type that represents a reference to Allocations,
137+
/// thus making it possible for the most convenient representation to be used in
138+
/// each context.
139+
///
140+
/// Defaults to the index based and loosely coupled AllocId.
136141
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
137-
pub struct Pointer {
138-
pub alloc_id: AllocId,
142+
pub struct Pointer<Id=AllocId> {
143+
pub alloc_id: Id,
139144
pub offset: Size,
140145
}
141146

@@ -543,16 +548,16 @@ impl Allocation {
543548
impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {}
544549

545550
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)]
546-
pub struct Relocations(SortedMap<Size, AllocId>);
551+
pub struct Relocations<Id=AllocId>(SortedMap<Size, Id>);
547552

548-
impl Relocations {
549-
pub fn new() -> Relocations {
553+
impl<Id> Relocations<Id> {
554+
pub fn new() -> Self {
550555
Relocations(SortedMap::new())
551556
}
552557

553558
// The caller must guarantee that the given relocations are already sorted
554559
// by address and contain no duplicates.
555-
pub fn from_presorted(r: Vec<(Size, AllocId)>) -> Relocations {
560+
pub fn from_presorted(r: Vec<(Size, Id)>) -> Self {
556561
Relocations(SortedMap::from_presorted_elements(r))
557562
}
558563
}

src/librustc/mir/interpret/value.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ impl From<Pointer> for Scalar {
326326
/// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
327327
/// of a simple value or a pointer into another `Allocation`
328328
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
329-
pub enum Scalar {
329+
pub enum Scalar<Id=AllocId> {
330330
/// The raw bytes of a simple value.
331331
Bits {
332332
/// The first `size` bytes are the value.
@@ -338,12 +338,12 @@ pub enum Scalar {
338338
/// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
339339
/// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
340340
/// relocation and its associated offset together as a `Pointer` here.
341-
Ptr(Pointer),
341+
Ptr(Pointer<Id>),
342342
}
343343

344344
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
345-
pub enum ScalarMaybeUndef {
346-
Scalar(Scalar),
345+
pub enum ScalarMaybeUndef<Id=AllocId> {
346+
Scalar(Scalar<Id>),
347347
Undef,
348348
}
349349

src/librustc_data_structures/stable_hasher.rs

+17
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,23 @@ impl<T1, T2, T3, CTX> HashStable<CTX> for (T1, T2, T3)
281281
}
282282
}
283283

284+
impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
285+
where T1: HashStable<CTX>,
286+
T2: HashStable<CTX>,
287+
T3: HashStable<CTX>,
288+
T4: HashStable<CTX>,
289+
{
290+
fn hash_stable<W: StableHasherResult>(&self,
291+
ctx: &mut CTX,
292+
hasher: &mut StableHasher<W>) {
293+
let (ref _0, ref _1, ref _2, ref _3) = *self;
294+
_0.hash_stable(ctx, hasher);
295+
_1.hash_stable(ctx, hasher);
296+
_2.hash_stable(ctx, hasher);
297+
_3.hash_stable(ctx, hasher);
298+
}
299+
}
300+
284301
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
285302
default fn hash_stable<W: StableHasherResult>(&self,
286303
ctx: &mut CTX,

src/librustc_mir/const_eval.rs

+2
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
196196
}
197197
}
198198

199+
impl_stable_hash_for!(struct CompileTimeEvaluator {});
200+
199201
#[derive(Clone, Debug)]
200202
enum ConstEvalError {
201203
NeedsRfc(String),

src/librustc_mir/interpret/eval_context.rs

+33-107
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@
99
// except according to those terms.
1010

1111
use std::fmt::Write;
12-
use std::hash::{Hash, Hasher};
1312
use std::mem;
1413

1514
use rustc::hir::def_id::DefId;
1615
use rustc::hir::def::Def;
1716
use rustc::hir::map::definitions::DefPathData;
17+
use rustc::ich::StableHashingContext;
1818
use rustc::mir;
1919
use rustc::ty::layout::{
2020
self, Size, Align, HasDataLayout, LayoutOf, TyLayout
2121
};
2222
use rustc::ty::subst::{Subst, Substs};
2323
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
2424
use rustc::ty::query::TyCtxtAt;
25-
use rustc_data_structures::fx::{FxHashSet, FxHasher};
2625
use rustc_data_structures::indexed_vec::IndexVec;
26+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
2727
use rustc::mir::interpret::{
28-
GlobalId, Scalar, FrameInfo,
28+
GlobalId, Scalar, FrameInfo, AllocId,
2929
EvalResult, EvalErrorKind,
3030
ScalarMaybeUndef,
3131
truncate, sign_extend,
@@ -38,6 +38,8 @@ use super::{
3838
Memory, Machine
3939
};
4040

41+
use super::snapshot::InfiniteLoopDetector;
42+
4143
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
4244
/// Stores the `Machine` instance.
4345
pub machine: M,
@@ -95,7 +97,7 @@ pub struct Frame<'mir, 'tcx: 'mir> {
9597
/// The locals are stored as `Option<Value>`s.
9698
/// `None` represents a local that is currently dead, while a live local
9799
/// can either directly contain `Scalar` or refer to some part of an `Allocation`.
98-
pub locals: IndexVec<mir::Local, LocalValue>,
100+
pub locals: IndexVec<mir::Local, LocalValue<AllocId>>,
99101

100102
////////////////////////////////////////////////////////////////////////////////
101103
// Current position within the function
@@ -108,51 +110,25 @@ pub struct Frame<'mir, 'tcx: 'mir> {
108110
pub stmt: usize,
109111
}
110112

111-
impl<'mir, 'tcx: 'mir> Eq for Frame<'mir, 'tcx> {}
112-
113-
impl<'mir, 'tcx: 'mir> PartialEq for Frame<'mir, 'tcx> {
114-
fn eq(&self, other: &Self) -> bool {
115-
let Frame {
116-
mir: _,
117-
instance,
118-
span: _,
119-
return_to_block,
120-
return_place,
121-
locals,
122-
block,
123-
stmt,
124-
} = self;
125-
126-
// Some of these are constant during evaluation, but are included
127-
// anyways for correctness.
128-
*instance == other.instance
129-
&& *return_to_block == other.return_to_block
130-
&& *return_place == other.return_place
131-
&& *locals == other.locals
132-
&& *block == other.block
133-
&& *stmt == other.stmt
134-
}
135-
}
113+
impl<'a, 'mir, 'tcx: 'mir> HashStable<StableHashingContext<'a>> for Frame<'mir, 'tcx> {
114+
fn hash_stable<W: StableHasherResult>(
115+
&self,
116+
hcx: &mut StableHashingContext<'a>,
117+
hasher: &mut StableHasher<W>) {
136118

137-
impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> {
138-
fn hash<H: Hasher>(&self, state: &mut H) {
139119
let Frame {
140-
mir: _,
120+
mir,
141121
instance,
142-
span: _,
122+
span,
143123
return_to_block,
144124
return_place,
145125
locals,
146126
block,
147127
stmt,
148128
} = self;
149129

150-
instance.hash(state);
151-
return_to_block.hash(state);
152-
return_place.hash(state);
153-
locals.hash(state);
154-
block.hash(state);
155-
stmt.hash(state);
130+
(mir, instance, span, return_to_block).hash_stable(hcx, hasher);
131+
(return_place, locals, block, stmt).hash_stable(hcx, hasher);
156132
}
157133
}
158134

@@ -168,15 +144,27 @@ pub enum StackPopCleanup {
168144
None { cleanup: bool },
169145
}
170146

147+
impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup {
148+
fn hash_stable<W: StableHasherResult>(
149+
&self,
150+
hcx: &mut StableHashingContext<'a>,
151+
hasher: &mut StableHasher<W>) {
152+
match self {
153+
StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher),
154+
StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher),
155+
}
156+
}
157+
}
158+
171159
// State of a local variable
172160
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
173-
pub enum LocalValue {
161+
pub enum LocalValue<Id=AllocId> {
174162
Dead,
175163
// Mostly for convenience, we re-use the `Operand` type here.
176164
// This is an optimization over just always having a pointer here;
177165
// we can thus avoid doing an allocation when the local just stores
178166
// immediate values *and* never has its address taken.
179-
Live(Operand),
167+
Live(Operand<Id>),
180168
}
181169

182170
impl<'tcx> LocalValue {
@@ -195,72 +183,10 @@ impl<'tcx> LocalValue {
195183
}
196184
}
197185

198-
/// The virtual machine state during const-evaluation at a given point in time.
199-
type EvalSnapshot<'a, 'mir, 'tcx, M>
200-
= (M, Vec<Frame<'mir, 'tcx>>, Memory<'a, 'mir, 'tcx, M>);
201-
202-
pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
203-
/// The set of all `EvalSnapshot` *hashes* observed by this detector.
204-
///
205-
/// When a collision occurs in this table, we store the full snapshot in
206-
/// `snapshots`.
207-
hashes: FxHashSet<u64>,
208-
209-
/// The set of all `EvalSnapshot`s observed by this detector.
210-
///
211-
/// An `EvalSnapshot` will only be fully cloned once it has caused a
212-
/// collision in `hashes`. As a result, the detector must observe at least
213-
/// *two* full cycles of an infinite loop before it triggers.
214-
snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx, M>>,
215-
}
216-
217-
impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M>
218-
where M: Machine<'mir, 'tcx>,
219-
'tcx: 'a + 'mir,
220-
{
221-
fn default() -> Self {
222-
InfiniteLoopDetector {
223-
hashes: FxHashSet::default(),
224-
snapshots: FxHashSet::default(),
225-
}
226-
}
227-
}
228-
229-
impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
230-
where M: Machine<'mir, 'tcx>,
231-
'tcx: 'a + 'mir,
232-
{
233-
/// Returns `true` if the loop detector has not yet observed a snapshot.
234-
pub fn is_empty(&self) -> bool {
235-
self.hashes.is_empty()
236-
}
237-
238-
pub fn observe_and_analyze(
239-
&mut self,
240-
machine: &M,
241-
stack: &Vec<Frame<'mir, 'tcx>>,
242-
memory: &Memory<'a, 'mir, 'tcx, M>,
243-
) -> EvalResult<'tcx, ()> {
244-
let snapshot = (machine, stack, memory);
245-
246-
let mut fx = FxHasher::default();
247-
snapshot.hash(&mut fx);
248-
let hash = fx.finish();
249-
250-
if self.hashes.insert(hash) {
251-
// No collision
252-
return Ok(())
253-
}
254-
255-
if self.snapshots.insert((machine.clone(), stack.clone(), memory.clone())) {
256-
// Spurious collision or first cycle
257-
return Ok(())
258-
}
259-
260-
// Second cycle
261-
Err(EvalErrorKind::InfiniteLoop.into())
262-
}
263-
}
186+
impl_stable_hash_for!(enum self::LocalValue {
187+
Dead,
188+
Live(x),
189+
});
264190

265191
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> {
266192
#[inline]

src/librustc_mir/interpret/machine.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
use std::hash::Hash;
1616

1717
use rustc::hir::def_id::DefId;
18+
use rustc::ich::StableHashingContext;
1819
use rustc::mir::interpret::{Allocation, EvalResult, Scalar};
1920
use rustc::mir;
2021
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
22+
use rustc_data_structures::stable_hasher::HashStable;
2123

2224
use super::{EvalContext, PlaceTy, OpTy};
2325

2426
/// Methods of this trait signifies a point where CTFE evaluation would fail
2527
/// and some use case dependent behaviour can instead be applied
26-
pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash {
28+
pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash + for<'a> HashStable<StableHashingContext<'a>> {
2729
/// Additional data that can be accessed via the Memory
28-
type MemoryData: Clone + Eq + Hash;
30+
type MemoryData: Clone + Eq + Hash + for<'a> HashStable<StableHashingContext<'a>>;
2931

3032
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
3133
type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq + Hash;

0 commit comments

Comments
 (0)