Skip to content

Commit

Permalink
add support for lifetime rewrites
Browse files Browse the repository at this point in the history
  • Loading branch information
aneksteind committed May 8, 2023
1 parent fe4c130 commit 1199a8f
Show file tree
Hide file tree
Showing 7 changed files with 588 additions and 332 deletions.
9 changes: 3 additions & 6 deletions c2rust-analyze/src/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,12 @@ pub fn borrowck_mir<'tcx>(
hypothesis: &mut PointerTableMut<PermissionSet>,
name: &str,
mir: &Body<'tcx>,
adt_metadata: &AdtMetadataTable<'tcx>,
field_ltys: HashMap<DefId, crate::LTy<'tcx>>,
) {
let mut i = 0;
loop {
eprintln!("run polonius");
let (facts, maps, output) =
run_polonius(acx, hypothesis, name, mir, adt_metadata, &field_ltys);
let (facts, maps, output) = run_polonius(acx, hypothesis, name, mir, &field_ltys);
eprintln!(
"polonius: iteration {}: {} errors, {} move_errors",
i,
Expand Down Expand Up @@ -200,7 +198,6 @@ fn run_polonius<'tcx>(
hypothesis: &PointerTableMut<PermissionSet>,
name: &str,
mir: &Body<'tcx>,
adt_metadata: &AdtMetadataTable<'tcx>,
field_ltys: &HashMap<DefId, crate::LTy<'tcx>>,
) -> (AllFacts, AtomMaps<'tcx>, Output) {
let tcx = acx.tcx();
Expand Down Expand Up @@ -274,7 +271,7 @@ fn run_polonius<'tcx>(
hypothesis,
&mut facts,
&mut maps,
adt_metadata,
&acx.gacx.adt_metadata,
acx.local_tys[local],
);
let var = maps.variable(local);
Expand Down Expand Up @@ -310,7 +307,7 @@ fn run_polonius<'tcx>(
&local_ltys,
&field_permissions,
mir,
adt_metadata,
&acx.gacx.adt_metadata,
&acx.c_void_casts,
);

Expand Down
273 changes: 271 additions & 2 deletions c2rust-analyze/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::borrowck::{AdtMetadata, FieldMetadata, OriginArg, OriginParam};
use crate::c_void_casts::CVoidCasts;
use crate::labeled_ty::{LabeledTy, LabeledTyCtxt};
use crate::pointer_id::{
Expand All @@ -6,16 +7,24 @@ use crate::pointer_id::{
};
use crate::util::{self, describe_rvalue, PhantomLifetime, RvalueDesc};
use crate::AssignPointerIds;
use assert_matches::assert_matches;
use bitflags::bitflags;
use rustc_hir::def_id::DefId;
use indexmap::IndexSet;
use rustc_ast::Mutability;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_index::vec::IndexVec;
use rustc_middle::mir::interpret::{self, AllocId, ConstValue, GlobalAlloc};
use rustc_middle::mir::{
Body, Constant, ConstantKind, Field, HasLocalDecls, Local, LocalDecls, Location, Operand,
Place, PlaceElem, PlaceRef, Rvalue,
};
use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyCtxt, TyKind};
use rustc_middle::ty::{
tls, AdtDef, FieldDef, GenericArgKind, GenericParamDefKind, Ty, TyCtxt, TyKind,
};
use rustc_type_ir::RegionKind::{ReEarlyBound, ReStatic};
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Index;

bitflags! {
Expand Down Expand Up @@ -149,6 +158,83 @@ pub struct LFnSig<'tcx> {
pub output: LTy<'tcx>,
}

pub struct AdtMetadataTable<'tcx> {
pub table: HashMap<DefId, AdtMetadata<'tcx>>,
pub struct_dids: Vec<DefId>,
}

impl<'tcx> Debug for AdtMetadataTable<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_string(lty: LabeledTy<'_, &[OriginArg]>) -> String {
let args: Vec<String> = lty.args.iter().map(|t| fmt_string(t)).collect();
use rustc_type_ir::TyKind::*;
match lty.kind() {
Ref(..) | RawPtr(..) => {
format!("&{:?} {:}", lty.label[0], args[0])
}
Adt(adt, _) => {
let mut s = format!("{adt:?}");
let params = lty
.label
.iter()
.map(|p| format!("{:?}", p))
.into_iter()
.chain(args.into_iter())
.collect::<Vec<_>>()
.join(",");

if !params.is_empty() {
s.push('<');
s.push_str(&params);
s.push('>');
}
s
}
Tuple(_) => {
format!("({:})", args.join(","))
}
_ => format!("{:?}", lty.ty),
}
}

tls::with_opt(|tcx| {
let tcx = tcx.unwrap();
for k in &self.struct_dids {
let adt = &self.table[k];
let other_param_names = tcx.generics_of(k).params.iter().filter_map(|p| {
if !matches!(p.kind, GenericParamDefKind::Lifetime) {
Some(p.name.to_ident_string())
} else {
None
}
});
write!(f, "struct {:}", tcx.item_name(*k))?;
write!(f, "<")?;
let lifetime_params_str = adt
.lifetime_params
.iter()
.map(|p| format!("{:?}", p))
.chain(other_param_names)
.collect::<Vec<_>>()
.join(",");
write!(f, "{lifetime_params_str:}")?;
writeln!(f, "> {{")?;
for (fdid, fmeta) in &adt.field_info {
write!(f, "\t{:}: ", tcx.item_name(*fdid))?;
let field_string_lty = fmt_string(fmeta.origin_args);

write!(f, "{field_string_lty:}")?;

writeln!(f)?;
}

writeln!(f, "}}\n")?;
}
writeln!(f)
})
}
}

pub struct GlobalAnalysisCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
pub lcx: LTyCtxt<'tcx>,
Expand All @@ -159,6 +245,8 @@ pub struct GlobalAnalysisCtxt<'tcx> {

pub static_tys: HashMap<DefId, LTy<'tcx>>,

pub adt_metadata: AdtMetadataTable<'tcx>,

next_ptr_id: NextGlobalPointerId,
}

Expand Down Expand Up @@ -216,6 +304,185 @@ pub struct AnalysisCtxtData<'tcx> {
next_ptr_id: NextLocalPointerId,
}

fn construct_adt_metadata<'tcx>(tcx: TyCtxt<'tcx>) -> AdtMetadataTable {
let struct_dids: Vec<_> = tcx
.hir_crate_items(())
.definitions()
.filter_map(|ldid: LocalDefId| {
use DefKind::*;
let did = ldid.to_def_id();
if matches!(tcx.def_kind(did), Struct | Enum | Union) {
return Some(did);
}

None
})
.collect();

let mut adt_metadata_table = AdtMetadataTable {
table: HashMap::new(),
struct_dids,
};

// Gather known lifetime parameters for each struct
for struct_did in &adt_metadata_table.struct_dids {
let struct_ty = tcx.type_of(struct_did);
if let TyKind::Adt(adt_def, substs) = struct_ty.kind() {
adt_metadata_table
.table
.insert(adt_def.did(), AdtMetadata::default());
eprintln!("gathering known lifetimes for {adt_def:?}");
for sub in substs.iter() {
if let GenericArgKind::Lifetime(r) = sub.unpack() {
eprintln!("\tfound lifetime {r:?} in {adt_def:?}");
assert_matches!(r.kind(), ReEarlyBound(eb) => {
let _ = adt_metadata_table
.table
.entry(adt_def.did())
.and_modify(|metadata| {
metadata.lifetime_params.insert(OriginParam::Actual(eb));
});
});
}
}
} else {
panic!("{struct_ty:?} is not a struct");
}
}

let ltcx = LabeledTyCtxt::<'tcx, &[OriginArg<'tcx>]>::new(tcx);
let mut loop_count = 0;
loop {
/*
This loop iterates over all structs and gathers metadata for each.
If there were no recursive or mutually-recursive data structures,
this loop would only need one iteration to complete. To support
recursive and mutually-recursive structs, the loop iterates until
the metadata gathered for each struct reaches a fixed point.
*/
loop_count += 1;
assert!(loop_count < 1000);

eprintln!("---- running fixed point struct field analysis iteration #{loop_count:?} ----");
let old_adt_metadata = adt_metadata_table.table.clone();
let mut next_hypo_origin_id = 0;

// for each struct, gather lifetime information (actual and hypothetical)
for struct_did in &adt_metadata_table.struct_dids {
let adt_def = tcx.adt_def(struct_did);
eprintln!("gathering lifetimes and lifetime parameters for {adt_def:?}");
for field in adt_def.all_fields() {
let field_ty: Ty = tcx.type_of(field.did);
eprintln!("\t{adt_def:?}.{:}", field.name);
let field_origin_args = ltcx.label(field_ty, &mut |ty| {
let mut field_origin_args = IndexSet::new();
match ty.kind() {
TyKind::RawPtr(ty) => {
eprintln!(
"\t\tfound pointer that requires hypothetical lifetime: *{:}",
if let Mutability::Mut = ty.mutbl {
"mut"
} else {
"const"
}
);
adt_metadata_table
.table
.entry(*struct_did)
.and_modify(|adt| {
let origin_arg = OriginArg::Hypothetical(next_hypo_origin_id);
let origin_param =
OriginParam::Hypothetical(next_hypo_origin_id);
eprintln!(
"\t\t\tinserting origin {origin_param:?} into {adt_def:?}"
);

adt.lifetime_params.insert(origin_param);
next_hypo_origin_id += 1;
field_origin_args.insert(origin_arg);
});
}
TyKind::Ref(reg, _ty, _mutability) => {
eprintln!("\t\tfound reference field lifetime: {reg:}");
assert_matches!(reg.kind(), ReEarlyBound(..) | ReStatic);
let origin_arg = OriginArg::Actual(*reg);
adt_metadata_table
.table
.entry(*struct_did)
.and_modify(|adt| {
if let ReEarlyBound(eb) = reg.kind() {
eprintln!("\t\t\tinserting origin {eb:?} into {adt_def:?}");
adt.lifetime_params.insert(OriginParam::Actual(eb));
}

field_origin_args.insert(origin_arg);
});
}
TyKind::Adt(adt_field, substs) => {
eprintln!("\t\tfound ADT field base type: {adt_field:?}");
for sub in substs.iter() {
if let GenericArgKind::Lifetime(r) = sub.unpack() {
eprintln!("\tfound field lifetime {r:?} in {adt_def:?}.{adt_field:?}");
eprintln!("\t\t\tinserting {adt_field:?} lifetime param {r:?} into {adt_def:?}.{:} lifetime parameters", field.name);
assert_matches!(r.kind(), ReEarlyBound(..) | ReStatic);
field_origin_args.insert(OriginArg::Actual(r));
}
}
if let Some(adt_field_metadata) =
adt_metadata_table.table.get(&adt_field.did()).cloned()
{
// add a metadata entry for the struct field matching the metadata entry
// for the struct definition of said field
adt_metadata_table
.table
.insert(field.did, adt_field_metadata.clone());

for adt_field_lifetime_param in adt_field_metadata.lifetime_params.iter() {
adt_metadata_table.table.entry(*struct_did).and_modify(|adt| {
if let OriginParam::Hypothetical(h) = adt_field_lifetime_param {
eprintln!("\t\t\tbubbling {adt_field:?} origin {adt_field_lifetime_param:?} up into {adt_def:?} origins");
field_origin_args.insert(OriginArg::Hypothetical(*h));
adt.lifetime_params.insert(*adt_field_lifetime_param);
}
});
}
}
}
_ => (),
}

if field_origin_args.is_empty() {
return &[];
}
let field_origin_args: Vec<_> = field_origin_args.into_iter().collect();
ltcx.arena().alloc_slice(&field_origin_args[..])
});

adt_metadata_table
.table
.entry(*struct_did)
.and_modify(|adt| {
adt.field_info.insert(
field.did,
FieldMetadata {
origin_args: field_origin_args,
},
);
});
}

eprintln!();
}

if adt_metadata_table.table == old_adt_metadata {
eprintln!("reached a fixed point in struct lifetime reconciliation\n");
break;
}
}

adt_metadata_table
}

impl<'tcx> GlobalAnalysisCtxt<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> GlobalAnalysisCtxt<'tcx> {
GlobalAnalysisCtxt {
Expand All @@ -224,6 +491,7 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
fn_sigs: HashMap::new(),
field_ltys: HashMap::new(),
static_tys: HashMap::new(),
adt_metadata: construct_adt_metadata(tcx),
next_ptr_id: NextGlobalPointerId::new(),
}
}
Expand Down Expand Up @@ -263,6 +531,7 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
ref mut field_ltys,
ref mut static_tys,
ref mut next_ptr_id,
adt_metadata: _,
} = *self;

for sig in fn_sigs.values_mut() {
Expand Down
Loading

0 comments on commit 1199a8f

Please sign in to comment.