Skip to content

Commit 8bc972c

Browse files
committed
Add option to extract opaque bodies
1 parent 7e58ea8 commit 8bc972c

20 files changed

+435
-64
lines changed

charon/src/cli_options.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ performs: `y := (x as E2).1`). Producing a better reconstruction is non-trivial.
110110
#[clap(long = "opaque")]
111111
#[serde(default)]
112112
pub opaque_modules: Vec<String>,
113+
/// Usually we skip the bodies of foreign methods and structs with private fields. When this
114+
/// flag is on, we don't.
115+
#[clap(long = "extract-opaque-bodies")]
116+
#[serde(default)]
117+
pub extract_opaque_bodies: bool,
113118
/// Do not provide a Rust version argument to Cargo (e.g., `+nightly-2022-01-29`).
114119
/// This is for Nix: outside of Nix, we use Rustup to call the proper version
115120
/// of Cargo (and thus need this argument), but within Nix we build and call a very

charon/src/get_mir.rs

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Various utilities to load MIR.
22
//! Allow to easily load the MIR code generated by a specific pass.
33
4-
use rustc_hir::def_id::{DefId, LocalDefId};
4+
use rustc_hir::def_id::DefId;
55
use rustc_middle::mir::Body;
66
use rustc_middle::ty::TyCtxt;
77

@@ -39,32 +39,40 @@ pub fn boxes_are_desugared(level: MirLevel) -> bool {
3939
}
4040
}
4141

42-
/// Query the MIR for a function at a specific level
42+
/// Query the MIR for a function at a specific level. Return `None` in the case of a foreign body
43+
/// with no MIR available (e.g. because it is not available for inlining).
4344
pub fn get_mir_for_def_id_and_level(
4445
tcx: TyCtxt<'_>,
45-
def_id: LocalDefId,
46+
def_id: DefId,
4647
level: MirLevel,
47-
) -> Body<'_> {
48+
) -> Option<Body<'_>> {
4849
// Below: we **clone** the bodies to make sure we don't have issues with
4950
// locked values (we had in the past).
50-
match level {
51-
MirLevel::Built => {
52-
let body = tcx.mir_built(def_id);
53-
// We clone to be sure there are no problems with locked values
54-
body.borrow().clone()
55-
}
56-
MirLevel::Promoted => {
57-
let (body, _) = tcx.mir_promoted(def_id);
58-
// We clone to be sure there are no problems with locked values
59-
body.borrow().clone()
51+
let body = if let Some(local_def_id) = def_id.as_local() {
52+
match level {
53+
MirLevel::Built => {
54+
let body = tcx.mir_built(local_def_id);
55+
// We clone to be sure there are no problems with locked values
56+
body.borrow().clone()
57+
}
58+
MirLevel::Promoted => {
59+
let (body, _) = tcx.mir_promoted(local_def_id);
60+
// We clone to be sure there are no problems with locked values
61+
body.borrow().clone()
62+
}
63+
MirLevel::Optimized => tcx.optimized_mir(def_id).clone(),
6064
}
61-
MirLevel::Optimized => {
62-
let def_id = DefId {
63-
krate: rustc_hir::def_id::LOCAL_CRATE,
64-
index: def_id.local_def_index,
65-
};
66-
// We clone to be sure there are no problems with locked values
65+
} else {
66+
// There are only two MIRs we can fetch for non-local bodies: CTFE mir for globals and
67+
// const fns, and optimized MIR for inlinable functions. The rest don't have MIR in the
68+
// rlib.
69+
if tcx.is_mir_available(def_id) {
6770
tcx.optimized_mir(def_id).clone()
71+
} else if tcx.is_ctfe_mir_available(def_id) {
72+
tcx.mir_for_ctfe(def_id).clone()
73+
} else {
74+
return None;
6875
}
69-
}
76+
};
77+
Some(body)
7078
}

charon/src/translate_crate_to_ullbc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ pub fn translate<'tcx, 'ctx>(
255255
errors_as_warnings: options.errors_as_warnings,
256256
error_count: 0,
257257
no_code_duplication: options.no_code_duplication,
258+
extract_opaque_bodies: options.extract_opaque_bodies,
258259
all_ids: LinkedHashSet::new(),
259260
stack: BTreeSet::new(),
260261
def_id: None,

charon/src/translate_ctx.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ pub struct TransCtx<'tcx, 'ctx> {
218218
/// reconstruction (note that because several patterns in a match may lead
219219
/// to the same branch, it is node always possible not to duplicate code).
220220
pub no_code_duplication: bool,
221+
/// Whether to extract the bodies of foreign methods and structs with private fields.
222+
pub extract_opaque_bodies: bool,
221223
/// All the ids, in the order in which we encountered them
222224
pub all_ids: LinkedHashSet<AnyTransId>,
223225
/// The declarations we came accross and which we haven't translated yet.

charon/src/translate_functions_to_ullbc.rs

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::ullbc_ast::*;
1515
use crate::values::*;
1616
use hax_frontend_exporter as hax;
1717
use hax_frontend_exporter::SInto;
18-
use rustc_hir::def_id::{DefId, LocalDefId};
18+
use rustc_hir::def_id::DefId;
1919
use rustc_middle::mir::START_BLOCK;
2020
use rustc_middle::ty;
2121
use translate_types::translate_bound_region_kind_name;
@@ -1429,16 +1429,6 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
14291429

14301430
let mut t_args: Vec<Operand> = Vec::new();
14311431
for arg in args {
1432-
// There should only be moved arguments, or constants
1433-
match arg {
1434-
hax::Operand::Move(_) | hax::Operand::Constant(_) => {
1435-
// OK
1436-
}
1437-
hax::Operand::Copy(_) => {
1438-
unreachable!();
1439-
}
1440-
}
1441-
14421432
// Translate
14431433
let op = self.translate_operand(span, arg)?;
14441434
t_args.push(op);
@@ -1447,11 +1437,21 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
14471437
Ok(t_args)
14481438
}
14491439

1450-
fn translate_body(mut self, local_id: LocalDefId, arg_count: usize) -> Result<ExprBody, Error> {
1440+
fn translate_body(
1441+
mut self,
1442+
rust_id: DefId,
1443+
arg_count: usize,
1444+
) -> Result<Option<ExprBody>, Error> {
14511445
let tcx = self.t_ctx.tcx;
14521446

1447+
if !rust_id.is_local() && !self.t_ctx.extract_opaque_bodies {
1448+
// We only extract non-local bodies if the `extract_opaque_bodies` option is set.
1449+
return Ok(None);
1450+
}
1451+
14531452
// Retrive the body
1454-
let body = get_mir_for_def_id_and_level(tcx, local_id, self.t_ctx.mir_level);
1453+
let Some(body) = get_mir_for_def_id_and_level(tcx, rust_id, self.t_ctx.mir_level)
1454+
else { return Ok(None) };
14551455

14561456
// Here, we have to create a MIR state, which contains the body
14571457
let state = hax::state::State::new_from_mir(
@@ -1462,7 +1462,7 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
14621462
// Yes, we have to clone, this is annoying: we end up cloning the body twice
14631463
body.clone(),
14641464
// Owner id
1465-
local_id.to_def_id(),
1465+
rust_id,
14661466
);
14671467
// Translate
14681468
let body: hax::MirBody<()> = body.sinto(&state);
@@ -1488,12 +1488,12 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
14881488
}
14891489

14901490
// Create the body
1491-
Ok(ExprBody {
1491+
Ok(Some(ExprBody {
14921492
meta,
14931493
arg_count,
14941494
locals: self.vars,
14951495
body: blocks,
1496-
})
1496+
}))
14971497
}
14981498

14991499
/// Translate a function's signature, and initialize a body translation context
@@ -1795,18 +1795,14 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
17951795
let signature = bt_ctx.translate_function_signature(rust_id)?;
17961796

17971797
// Check if the type is opaque or transparent
1798-
let is_local = rust_id.is_local();
1799-
1800-
let body = if !is_transparent || !is_local || is_trait_method_decl {
1801-
None
1802-
} else {
1803-
match bt_ctx.translate_body(rust_id.expect_local(), signature.inputs.len()) {
1804-
Ok(body) => Some(body),
1805-
Err(_) => {
1806-
// Error case: we could have a variant for this
1807-
None
1808-
}
1798+
let body = if is_transparent && !is_trait_method_decl {
1799+
match bt_ctx.translate_body(rust_id, signature.inputs.len()) {
1800+
Ok(body) => body,
1801+
// Error case: we could have a variant for this
1802+
Err(_) => None,
18091803
}
1804+
} else {
1805+
None
18101806
};
18111807

18121808
// Save the new function
@@ -1816,7 +1812,7 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
18161812
meta,
18171813
def_id,
18181814
rust_id,
1819-
is_local,
1815+
is_local: rust_id.is_local(),
18201816
name,
18211817
signature,
18221818
kind,
@@ -1887,14 +1883,12 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
18871883
let generics = bt_ctx.get_generics();
18881884
let preds = bt_ctx.get_predicates();
18891885

1890-
let body = if rust_id.is_local() && is_transparent {
1891-
// It's a local and transparent global: we extract its body as for functions.
1892-
match bt_ctx.translate_body(rust_id.expect_local(), 0) {
1893-
Err(_) => {
1894-
// Error case: we could have a specific variant
1895-
None
1896-
}
1897-
Ok(body) => Some(body),
1886+
let body = if is_transparent {
1887+
// It's a transparent global: we extract its body as for functions.
1888+
match bt_ctx.translate_body(rust_id, 0) {
1889+
Ok(body) => body,
1890+
// Error case: we could have a specific variant
1891+
Err(_) => None,
18981892
}
18991893
} else {
19001894
// Otherwise do nothing

charon/src/translate_traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
304304
// but still remember their name.
305305
if has_default_value {
306306
// This is a *provided* method
307-
if rust_id.is_local() {
307+
if rust_id.is_local() || bt_ctx.t_ctx.extract_opaque_bodies {
308308
let fun_id = bt_ctx.translate_fun_decl_id(span, item.def_id);
309309
provided_methods.push((method_name, Some(fun_id)));
310310
} else {

charon/src/translate_types.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,11 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
500500
/// struct with only public fields).
501501
fn translate_type_body(
502502
&mut self,
503-
is_local: bool,
504503
trans_id: TypeDeclId::Id,
505504
rust_id: DefId,
506505
) -> Result<TypeDeclKind, Error> {
507506
use rustc_middle::ty::AdtKind;
507+
let is_local = rust_id.is_local();
508508
let def_span = self.t_ctx.tcx.def_span(rust_id);
509509
// Don't use `hax::AdtDef` because it loses `VariantIdx` information.
510510
let adt: rustc_middle::ty::AdtDef = self.t_ctx.tcx.adt_def(rust_id);
@@ -514,7 +514,8 @@ impl<'tcx, 'ctx, 'ctx1> BodyTransCtx<'tcx, 'ctx, 'ctx1> {
514514
// transparent (i.e., extract its body). If it is an enumeration, then yes
515515
// (because the variants of public enumerations are public, together with their
516516
// fields). If it is a structure, we check if all the fields are public.
517-
let is_transparent = is_local
517+
let is_transparent = self.t_ctx.extract_opaque_bodies
518+
|| is_local
518519
|| match adt.adt_kind() {
519520
AdtKind::Enum => true,
520521
AdtKind::Struct => {
@@ -717,7 +718,6 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
717718
fn translate_type_aux(&mut self, rust_id: DefId) -> Result<(), Error> {
718719
let trans_id = self.translate_type_decl_id(&None, rust_id);
719720
let is_transparent = self.id_is_transparent(rust_id)?;
720-
721721
let mut bt_ctx = BodyTransCtx::new(rust_id, self);
722722

723723
// Check and translate the generics
@@ -733,11 +733,10 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
733733
// For instance, because `core::option::Option` is public, we can
734734
// manipulate its variants. If we encounter this type, we must retrieve
735735
// its definition.
736-
let is_local = rust_id.is_local();
737736
let kind = if !is_transparent {
738737
TypeDeclKind::Opaque
739738
} else {
740-
match bt_ctx.translate_type_body(is_local, trans_id, rust_id) {
739+
match bt_ctx.translate_type_body(trans_id, rust_id) {
741740
Ok(kind) => kind,
742741
Err(err) => TypeDeclKind::Error(err.msg),
743742
}
@@ -753,7 +752,7 @@ impl<'tcx, 'ctx> TransCtx<'tcx, 'ctx> {
753752
let type_def = TypeDecl {
754753
def_id: trans_id,
755754
meta,
756-
is_local,
755+
is_local: rust_id.is_local(),
757756
name,
758757
generics,
759758
preds: bt_ctx.get_predicates(),

charon/tests/ui/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.rlib
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ skip
2+
#[inline(always)]
3+
pub fn inline_always() -> u32 {
4+
42
5+
}
6+
7+
#[inline]
8+
pub fn inline_sometimes() -> u32 {
9+
42
10+
}
11+
12+
#[inline(never)]
13+
pub fn inline_never() -> u32 {
14+
42
15+
}
16+
17+
// Generics always have MIR in the crate data.
18+
#[inline(never)]
19+
pub fn inline_generic<T>() -> u32 {
20+
42
21+
}

0 commit comments

Comments
 (0)