Skip to content

Commit 0856e02

Browse files
authored
Fail in case of missing resolve data for mutable borrows (#1690)
2 parents dc27121 + c3fed47 commit 0856e02

File tree

5 files changed

+68
-63
lines changed

5 files changed

+68
-63
lines changed

creusot/src/analysis.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ type BorrowId = usize;
5454

5555
/// Information computed by this analysis.
5656
#[derive(TyEncodable, TyDecodable, Clone, Debug)]
57-
pub struct BodyData<'tcx> {
57+
pub struct BorrowData<'tcx> {
5858
/// Resolves before each statement and terminator
5959
///
6060
/// For `Call` terminators, they are split in a `Call` statement and a `Goto` terminator,
@@ -72,9 +72,9 @@ pub struct BodyData<'tcx> {
7272
two_phase_activated: HashMap<Orphan<Location>, Vec<(Place<'tcx>, Place<'tcx>, BorrowKind)>>,
7373
}
7474

75-
impl<'tcx> BodyData<'tcx> {
75+
impl<'tcx> BorrowData<'tcx> {
7676
pub fn new() -> Self {
77-
BodyData {
77+
BorrowData {
7878
resolved_at: HashMap::new(),
7979
resolved_between_blocks: HashMap::new(),
8080
two_phase_created: HashSet::new(),
@@ -262,7 +262,7 @@ pub struct Analysis<'a, 'tcx> {
262262
not_final_places: ResultsCursor<'a, 'tcx, NotFinalPlaces<'tcx>>,
263263
/// `&mut` because we also rename assertions here
264264
body_specs: &'a mut BodySpecs<'tcx>,
265-
data: BodyData<'tcx>,
265+
data: BorrowData<'tcx>,
266266
}
267267

268268
impl<'a, 'tcx> HasTyCtxt<'tcx> for Analysis<'a, 'tcx> {
@@ -305,7 +305,7 @@ impl<'a, 'tcx> Analysis<'a, 'tcx> {
305305
not_final_places: NotFinalPlaces::new(tcx, &body.body)
306306
.iterate_to_fixpoint(tcx, &body.body, None)
307307
.into_results_cursor(&body.body),
308-
data: BodyData::new(),
308+
data: BorrowData::new(),
309309
body_specs,
310310
}
311311
}
@@ -852,7 +852,10 @@ impl<'a, 'tcx> Analysis<'a, 'tcx> {
852852
/// Analysis to run from crates that don't have access to creusot-contracts.
853853
// TODO: this will be used very soon
854854
#[allow(dead_code)]
855-
pub(crate) fn run_without_specs<'a, 'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> BodyData<'tcx> {
855+
pub(crate) fn run_without_specs<'a, 'tcx>(
856+
tcx: TyCtxt<'tcx>,
857+
def_id: LocalDefId,
858+
) -> BorrowData<'tcx> {
856859
let body = callbacks::get_body(tcx, def_id);
857860
let mut body_specs = BodySpecs::empty();
858861
let corenamer = HashMap::new();
@@ -869,7 +872,7 @@ pub(crate) fn run_with_specs<'a, 'tcx>(
869872
ctx: &TranslationCtx<'tcx>,
870873
body: &'a BodyWithBorrowckFacts<'tcx>,
871874
body_specs: &mut BodySpecs<'tcx>,
872-
) -> BodyData<'tcx> {
875+
) -> BorrowData<'tcx> {
873876
let tcx = ctx.tcx;
874877
let corenamer = &ctx.corenamer.borrow();
875878
// We take `locals` from `body_specs` and put it back later

creusot/src/translation/function.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod statement;
22
mod terminator;
33

44
use crate::{
5-
analysis::{self, BodyData, BodySpecs},
5+
analysis::{self, BodySpecs, BorrowData},
66
backend::ty_inv::is_tyinv_trivial,
77
ctx::*,
88
gather_spec_closures::LoopSpecKind,
@@ -35,7 +35,7 @@ struct BodyTranslator<'a, 'tcx> {
3535
body_id: BodyId,
3636

3737
body: &'a Body<'tcx>,
38-
body_data: BodyData<'tcx>,
38+
borrow_data: Option<BorrowData<'tcx>>,
3939
typing_env: TypingEnv<'tcx>,
4040

4141
/// Current block being generated
@@ -94,26 +94,26 @@ impl<'body, 'tcx> BodyTranslator<'body, 'tcx> {
9494
body_id: BodyId,
9595
f: F,
9696
) -> R {
97-
let (body, body_specs, body_data) = match body_id.def_id.as_local() {
97+
let (body, body_specs, borrow_data) = match body_id.def_id.as_local() {
9898
Some(def_id) => {
9999
let body_with_facts = ctx.body_with_facts(def_id);
100100
let body = match body_id.promoted {
101101
None => &body_with_facts.body,
102102
Some(promoted) => body_with_facts.promoted.get(promoted).unwrap(),
103103
};
104104
let mut body_specs = analysis::BodySpecs::from_body(ctx, body);
105-
let body_data = match body_id.promoted {
106-
None => analysis::run_with_specs(ctx, &body_with_facts, &mut body_specs),
107-
Some(_) => BodyData::new(),
105+
let borrow_data = match body_id.promoted {
106+
None => Some(analysis::run_with_specs(ctx, &body_with_facts, &mut body_specs)),
107+
Some(_) => None,
108108
};
109-
(body, body_specs, body_data)
109+
(body, body_specs, borrow_data)
110110
}
111111
None => {
112112
assert!(body_id.promoted.is_none());
113113
let body = ctx.tcx.mir_for_ctfe(body_id.def_id);
114114
let body_specs = analysis::BodySpecs::from_body(ctx, &body);
115-
let body_data = BodyData::new();
116-
(body, body_specs, body_data)
115+
let borrow_data = None;
116+
(body, body_specs, borrow_data)
117117
}
118118
};
119119
let typing_env = ctx.typing_env(body_id.def_id);
@@ -129,7 +129,7 @@ impl<'body, 'tcx> BodyTranslator<'body, 'tcx> {
129129
f(BodyTranslator {
130130
body,
131131
body_id,
132-
body_data,
132+
borrow_data,
133133
typing_env,
134134
locals,
135135
vars,
@@ -213,7 +213,10 @@ impl<'body, 'tcx> BodyTranslator<'body, 'tcx> {
213213
}
214214

215215
fn resolve_at(&mut self, loc: Location, span: Span) {
216-
for place in self.body_data.remove_resolved_places_at(loc) {
216+
let Some(borrow_data) = &mut self.borrow_data else {
217+
return;
218+
};
219+
for place in borrow_data.remove_resolved_places_at(loc) {
217220
self.emit_resolve(place, span);
218221
}
219222
}

creusot/src/translation/function/promoted.rs

Lines changed: 0 additions & 34 deletions
This file was deleted.

creusot/src/translation/function/statement.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use crate::{
99
pearlite::Term,
1010
},
1111
};
12+
use rustc_ast::Mutability;
1213
use rustc_middle::{
1314
mir::{
1415
BorrowKind::*, CastKind, Location, Operand::*, Place, Rvalue, SourceInfo, Statement,
1516
StatementKind,
1617
},
17-
ty::{Ty, TyKind, UintTy, adjustment::PointerCoercion},
18+
ty::{ConstKind, Ty, TyKind, UintTy, adjustment::PointerCoercion},
1819
};
1920
use rustc_span::Span;
2021

@@ -77,11 +78,18 @@ impl<'tcx> BodyTranslator<'_, 'tcx> {
7778
}
7879
RValue::Operand(Operand::Copy(self.translate_place(pl, span)))
7980
}
81+
// Special case to support const-promoted `&mut []`
82+
Mut { .. } if self.borrow_data.is_none() && is_mut_ref_empty(&ty) => {
83+
self.emit_borrow(place, pl, fmir::BorrowKind::Mut, span);
84+
// No need to resolve: the length of the final value is set to 0 by the type invariant
85+
return;
86+
}
8087
Mut { .. } => {
81-
if self.erased_locals.contains(pl.local) || self.is_two_phase(loc) {
88+
let Some(borrow_data) = &self.borrow_data else { self.error_no_borrow_data() };
89+
if self.erased_locals.contains(pl.local) || borrow_data.is_two_phase_at(loc) {
8290
return;
8391
}
84-
let is_final = self.is_final_at(loc);
92+
let is_final = borrow_data.is_final_at(loc);
8593
self.emit_borrow(place, pl, is_final, span);
8694
return;
8795
}
@@ -205,17 +213,40 @@ impl<'tcx> BodyTranslator<'_, 'tcx> {
205213
}
206214
}
207215

208-
fn is_two_phase(&self, loc: Location) -> bool {
209-
self.body_data.is_two_phase_at(loc)
210-
}
211-
212216
pub(super) fn activate_two_phase(&mut self, loc: Location, span: Span) {
213-
for (lhs, rhs, is_final) in self.body_data.remove_two_phase_activated_at(loc) {
217+
let Some(borrow_data) = &mut self.borrow_data else {
218+
return;
219+
};
220+
for (lhs, rhs, is_final) in borrow_data.remove_two_phase_activated_at(loc) {
214221
self.emit_borrow(lhs, rhs, is_final, span)
215222
}
216223
}
217224

218-
fn is_final_at(&self, loc: Location) -> fmir::BorrowKind {
219-
self.body_data.is_final_at(loc)
225+
/// Fail if mutable borrows are used and no borrow data is available.
226+
fn error_no_borrow_data(&self) -> ! {
227+
self.crash_and_error(
228+
self.body.span,
229+
format!(
230+
"can't translate {}: no data to resolve mutable borrows",
231+
self.tcx().def_path_str(self.body_id.def_id)
232+
),
233+
);
220234
}
221235
}
236+
237+
/// Check whether `ty` is of the form `&mut [T; 0]`
238+
fn is_mut_ref_empty<'tcx>(ty: &Ty<'tcx>) -> bool {
239+
let TyKind::Ref(_, ty, Mutability::Mut) = ty.kind() else {
240+
return false;
241+
};
242+
let TyKind::Array(_, len) = ty.kind() else {
243+
return false;
244+
};
245+
let ConstKind::Value(value) = len.kind() else {
246+
return false;
247+
};
248+
let Some(scalar) = value.valtree.try_to_scalar_int() else {
249+
return false;
250+
};
251+
scalar.is_null()
252+
}

creusot/src/translation/function/terminator.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ impl<'tcx> BodyTranslator<'_, 'tcx> {
184184
| TailCall { .. } => unreachable!("{:?}", terminator.kind),
185185
};
186186
// Insert blocks of resolve statements and retarget the terminator to those blocks.
187-
if let Some(mut resolved) = self.body_data.remove_resolved_between_blocks(loc.block) {
187+
if let Some(borrow_data) = &mut self.borrow_data
188+
&& let Some(mut resolved) = borrow_data.remove_resolved_between_blocks(loc.block)
189+
{
188190
let mut retarget = HashMap::new();
189191
for target in term.targets_mut() {
190192
match resolved.remove(target) {

0 commit comments

Comments
 (0)