Skip to content

Commit 31baf0a

Browse files
committed
Merge branch 'master' into kkysen/analyze-string-casts
2 parents 7f87dc1 + 6f1aeb8 commit 31baf0a

File tree

7 files changed

+315
-21
lines changed

7 files changed

+315
-21
lines changed

c2rust-analyze/src/main.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use crate::dataflow::DataflowConstraints;
2323
use crate::equiv::{GlobalEquivSet, LocalEquivSet};
2424
use crate::labeled_ty::LabeledTyCtxt;
2525
use crate::log::init_logger;
26-
use crate::util::Callee;
26+
use crate::panic_detail::PanicDetail;
27+
use crate::util::{Callee, TestAttr};
2728
use context::AdtMetadataTable;
2829
use rustc_hir::def::DefKind;
2930
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -38,6 +39,7 @@ use rustc_span::Span;
3839
use std::collections::{HashMap, HashSet};
3940
use std::env;
4041
use std::fmt::{Debug, Display};
42+
use std::iter;
4143
use std::ops::{Deref, DerefMut, Index};
4244
use std::panic::AssertUnwindSafe;
4345

@@ -493,6 +495,30 @@ fn run(tcx: TyCtxt) {
493495
info.lasn.set(lasn);
494496
}
495497

498+
// For testing, putting #[c2rust_analyze_test::fixed_signature] on a function makes all
499+
// pointers in its signature FIXED.
500+
for &ldid in &all_fn_ldids {
501+
if !util::has_test_attr(tcx, ldid, TestAttr::FixedSignature) {
502+
continue;
503+
}
504+
let lsig = match gacx.fn_sigs.get(&ldid.to_def_id()) {
505+
Some(x) => x,
506+
None => continue,
507+
};
508+
make_sig_fixed(&mut gasn, lsig);
509+
}
510+
511+
// For testing, putting #[c2rust_analyze_test::fail_analysis] on a function marks it as failed.
512+
for &ldid in &all_fn_ldids {
513+
if !util::has_test_attr(tcx, ldid, TestAttr::FailAnalysis) {
514+
continue;
515+
}
516+
gacx.mark_fn_failed(
517+
ldid.to_def_id(),
518+
PanicDetail::new("explicit fail_analysis for testing".to_owned()),
519+
);
520+
}
521+
496522
eprintln!("=== ADT Metadata ===");
497523
eprintln!("{:?}", gacx.adt_metadata);
498524

@@ -597,6 +623,10 @@ fn run(tcx: TyCtxt) {
597623

598624
eprintln!();
599625

626+
if util::has_test_attr(tcx, ldid, TestAttr::SkipRewrite) {
627+
continue;
628+
}
629+
600630
let r = panic_detail::catch_unwind(AssertUnwindSafe(|| {
601631
let hir_body_id = tcx.hir().body_owned_by(ldid);
602632
let expr_rewrites = rewrite::gen_expr_rewrites(&acx, &asn, &mir, hir_body_id);
@@ -738,6 +768,21 @@ impl<'tcx> AssignPointerIds<'tcx> for AnalysisCtxt<'_, 'tcx> {
738768
}
739769
}
740770

771+
fn make_ty_fixed(gasn: &mut GlobalAssignment, lty: LTy) {
772+
for lty in lty.iter() {
773+
let ptr = lty.label;
774+
if !ptr.is_none() {
775+
gasn.flags[ptr].insert(FlagSet::FIXED);
776+
}
777+
}
778+
}
779+
780+
fn make_sig_fixed(gasn: &mut GlobalAssignment, lsig: &LFnSig) {
781+
for lty in lsig.inputs.iter().copied().chain(iter::once(lsig.output)) {
782+
make_ty_fixed(gasn, lty);
783+
}
784+
}
785+
741786
fn describe_local(tcx: TyCtxt, decl: &LocalDecl) -> String {
742787
let mut span = decl.source_info.span;
743788
if let Some(ref info) = decl.local_info {

c2rust-analyze/src/rewrite/expr/hir_op.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,22 @@ impl<'a, 'tcx> HirRewriteVisitor<'a, 'tcx> {
197197
.unwrap_or_else(|err| panic_location_error(err, "Assignment statement"));
198198
locations.push(assign_loc)
199199
}
200+
hir::ExprKind::Cast(..) => {
201+
let r = self.find_sole_location_matching(
202+
ex.span,
203+
|stmt| {
204+
let rv = match stmt.kind {
205+
mir::StatementKind::Assign(ref x) => &x.1,
206+
_ => return false,
207+
};
208+
matches!(rv, mir::Rvalue::Cast(..))
209+
},
210+
|_term| false,
211+
);
212+
if let Ok(assign_loc) = r {
213+
locations.push(assign_loc);
214+
}
215+
}
200216
_ => eprintln!("warning: find_primary_location: unsupported expr {:?}", ex),
201217
}
202218

@@ -320,6 +336,21 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirRewriteVisitor<'a, 'tcx> {
320336
Rewrite::Ref(Box::new(self.get_subexpr(ex, 0)), mutbl_from_bool(*mutbl))
321337
}
322338

339+
mir_op::RewriteKind::CastRefToRaw { mutbl } => {
340+
// `addr_of!(*p)` is cleaner than `p as *const _`; we don't know the pointee
341+
// type here, so we can't emit `p as *const T`.
342+
let rw_pl = Rewrite::Deref(Box::new(hir_rw));
343+
Rewrite::AddrOf(Box::new(rw_pl), mutbl_from_bool(*mutbl))
344+
}
345+
mir_op::RewriteKind::CastRawToRaw { to_mutbl } => {
346+
let method = if *to_mutbl { "cast_mut" } else { "cast_const" };
347+
Rewrite::MethodCall(method.to_string(), Box::new(hir_rw), vec![])
348+
}
349+
mir_op::RewriteKind::UnsafeCastRawToRef { mutbl } => {
350+
let rw_pl = Rewrite::Deref(Box::new(hir_rw));
351+
Rewrite::Ref(Box::new(rw_pl), mutbl_from_bool(*mutbl))
352+
}
353+
323354
mir_op::RewriteKind::CellNew => {
324355
// `x` to `Cell::new(x)`
325356
Rewrite::Call("std::cell::Cell::new".to_string(), vec![Rewrite::Identity])
@@ -341,6 +372,14 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirRewriteVisitor<'a, 'tcx> {
341372
let rhs = self.get_subexpr(ex, 1);
342373
Rewrite::MethodCall("set".to_string(), Box::new(lhs), vec![rhs])
343374
}
375+
376+
mir_op::RewriteKind::CellFromMut => {
377+
// `x` to `Cell::from_mut(x)`
378+
Rewrite::Call(
379+
"std::cell::Cell::from_mut".to_string(),
380+
vec![Rewrite::Identity],
381+
)
382+
}
344383
}
345384
};
346385

c2rust-analyze/src/rewrite/expr/mir_op.rs

Lines changed: 160 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::panic_detail;
1212
use crate::pointer_id::PointerTable;
1313
use crate::type_desc::{self, Ownership, Quantity, TypeDesc};
1414
use crate::util::{ty_callee, Callee};
15+
use log::*;
1516
use rustc_ast::Mutability;
1617
use rustc_middle::mir::{
1718
BasicBlock, Body, Location, Operand, Place, Rvalue, Statement, StatementKind, Terminator,
@@ -51,13 +52,24 @@ pub enum RewriteKind {
5152
RemoveAsPtr,
5253
/// Replace &raw with & or &raw mut with &mut
5354
RawToRef { mutbl: bool },
55+
56+
/// Cast `&T` to `*const T` or `&mut T` to `*mut T`.
57+
CastRefToRaw { mutbl: bool },
58+
/// Cast `*const T` to `*mut T` or vice versa. If `to_mutbl` is true, we are casting to
59+
/// `*mut T`; otherwise, we're casting to `*const T`.
60+
CastRawToRaw { to_mutbl: bool },
61+
/// Cast `*const T` to `& T` or `*mut T` to `&mut T`.
62+
UnsafeCastRawToRef { mutbl: bool },
63+
5464
/// Replace `y` in `let x = y` with `Cell::new(y)`, i.e. `let x = Cell::new(y)`
5565
/// TODO: ensure `y` implements `Copy`
5666
CellNew,
5767
/// Replace `*y` with `Cell::get(y)` where `y` is a pointer
5868
CellGet,
5969
/// Replace `*y = x` with `Cell::set(x)` where `y` is a pointer
6070
CellSet,
71+
/// Wrap `&mut T` in `Cell::from_mut` to get `&Cell<T>`.
72+
CellFromMut,
6173
}
6274

6375
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -257,11 +269,9 @@ impl<'a, 'tcx> ExprRewriteVisitor<'a, 'tcx> {
257269
match ty_callee(tcx, func_ty) {
258270
Callee::PtrOffset { .. } => {
259271
self.visit_ptr_offset(&args[0], pl_ty);
260-
return;
261272
}
262273
Callee::SliceAsPtr { elem_ty, .. } => {
263274
self.visit_slice_as_ptr(elem_ty, &args[0], pl_ty);
264-
return;
265275
}
266276
_ => {}
267277
}
@@ -424,10 +434,9 @@ impl<'a, 'tcx> ExprRewriteVisitor<'a, 'tcx> {
424434

425435
self.emit(RewriteKind::OffsetSlice { mutbl });
426436

427-
// If the result is `Single`, also insert an upcast.
428-
if result_desc.qty == Quantity::Single {
429-
self.emit(RewriteKind::SliceFirst { mutbl });
430-
}
437+
// The `OffsetSlice` operation returns something of the same type as its input. Afterward,
438+
// we must cast the result to the `result_ty`/`result_desc`.
439+
self.emit_cast_desc_desc(arg_expect_desc, result_desc);
431440
}
432441

433442
fn visit_slice_as_ptr(&mut self, elem_ty: Ty<'tcx>, op: &Operand<'tcx>, result_lty: LTy<'tcx>) {
@@ -468,38 +477,172 @@ impl<'a, 'tcx> ExprRewriteVisitor<'a, 'tcx> {
468477
}
469478

470479
fn emit_cast_desc_desc(&mut self, from: TypeDesc<'tcx>, to: TypeDesc<'tcx>) {
480+
let orig_from = from;
481+
let mut from = orig_from;
482+
483+
// The `from` and `to` pointee types should only differ in their lifetimes.
471484
assert_eq!(
472485
self.acx.tcx().erase_regions(from.pointee_ty),
473486
self.acx.tcx().erase_regions(to.pointee_ty),
474487
);
488+
// There might still be differences in lifetimes, which we don't care about here.
489+
// Overwriting `from.pointee_ty` allows the final `from == to` check to succeed below.
490+
from.pointee_ty = to.pointee_ty;
475491

476492
if from == to {
477493
return;
478494
}
479495

480-
let orig_from = from;
481-
let mut from = orig_from;
496+
// Early `Ownership` casts. We do certain casts here in hopes of reaching an `Ownership`
497+
// on which we can safely adjust `Quantity`.
498+
from.own = self.cast_ownership(from, to, true);
499+
500+
// Safe casts that change `Quantity`.
501+
while from.qty != to.qty {
502+
// Mutability of `from`. `None` here means that safe `Quantity` conversions aren't
503+
// possible given `from`'s `Ownership`. For example, we can't convert `Box<[T]>` to
504+
// `Box<T>`.
505+
let opt_mutbl = match from.own {
506+
// Note that `Cell` + `Slice` is `&[Cell<T>]`, not `&Cell<[T]>`, so it can be
507+
// handled like any other `&[_]`.
508+
Ownership::Imm | Ownership::Cell => Some(false),
509+
Ownership::Mut => Some(true),
510+
_ => None,
511+
};
512+
match (from.qty, to.qty) {
513+
(Quantity::Array, _) => {
514+
// `Array` goes only to `Slice` directly. All other `Array` conversions go
515+
// through `Slice` first.
516+
error!("TODO: cast Array to {:?}", to.qty);
517+
from.qty = Quantity::Slice;
518+
}
519+
// Bidirectional conversions between `Slice` and `OffsetPtr`.
520+
(Quantity::Slice, Quantity::OffsetPtr) | (Quantity::OffsetPtr, Quantity::Slice) => {
521+
// Currently a no-op, since `Slice` and `OffsetPtr` are identical.
522+
from.qty = to.qty;
523+
}
524+
// `Slice` and `OffsetPtr` convert to `Single` the same way.
525+
// TODO: when converting to `Ownership::Raw`/`RawMut`, use `slice.as_ptr()` to
526+
// avoid panic on 0-length inputs
527+
(_, Quantity::Single) => {
528+
let rw = match opt_mutbl {
529+
Some(mutbl) => RewriteKind::SliceFirst { mutbl },
530+
None => break,
531+
};
532+
self.emit(rw);
533+
from.qty = Quantity::Single;
534+
}
482535

483-
if (from.qty, to.qty) == (Quantity::OffsetPtr, Quantity::Slice) {
484-
// TODO: emit rewrite
485-
from.qty = to.qty;
486-
}
536+
// Unsupported cases
537+
(Quantity::Single, _) => break,
538+
(_, Quantity::Array) => break,
487539

488-
if from.qty == to.qty && (from.own, to.own) == (Ownership::Mut, Ownership::Imm) {
489-
self.emit(RewriteKind::MutToImm);
490-
from.own = to.own;
540+
// Remaining cases are impossible, since `from.qty != to.qty`.
541+
(Quantity::Slice, Quantity::Slice) | (Quantity::OffsetPtr, Quantity::OffsetPtr) => {
542+
unreachable!()
543+
}
544+
}
491545
}
492546

493-
// TODO: handle Slice -> Single here instead of special-casing in `offset`
547+
// Late `Ownership` casts.
548+
from.own = self.cast_ownership(from, to, false);
494549

495550
if from != to {
496-
eprintln!(
551+
panic!(
497552
"unsupported cast kind: {:?} -> {:?} (original input: {:?})",
498553
from, to, orig_from
499554
);
500555
}
501556
}
502557

558+
fn cast_ownership(&mut self, from: TypeDesc, to: TypeDesc, early: bool) -> Ownership {
559+
let mut from = from;
560+
while from.own != to.own {
561+
match self.cast_ownership_one_step(from, to, early) {
562+
Some(new_own) => {
563+
from.own = new_own;
564+
}
565+
None => break,
566+
}
567+
}
568+
from.own
569+
}
570+
571+
fn cast_ownership_one_step(
572+
&mut self,
573+
from: TypeDesc,
574+
to: TypeDesc,
575+
early: bool,
576+
) -> Option<Ownership> {
577+
match from.own {
578+
Ownership::Box => match to.own {
579+
Ownership::Raw | Ownership::Imm => {
580+
error!("TODO: cast Box to Imm");
581+
Some(Ownership::Imm)
582+
}
583+
Ownership::RawMut | Ownership::Mut => {
584+
error!("TODO: cast Box to Mut");
585+
Some(Ownership::Mut)
586+
}
587+
_ => None,
588+
},
589+
Ownership::Rc => match to.own {
590+
Ownership::Imm | Ownership::Raw | Ownership::RawMut => {
591+
error!("TODO: cast Rc to Imm");
592+
Some(Ownership::Imm)
593+
}
594+
_ => None,
595+
},
596+
Ownership::Mut => match to.own {
597+
Ownership::Imm | Ownership::Raw => {
598+
self.emit(RewriteKind::MutToImm);
599+
Some(Ownership::Imm)
600+
}
601+
Ownership::Cell => {
602+
self.emit(RewriteKind::CellFromMut);
603+
Some(Ownership::Cell)
604+
}
605+
Ownership::RawMut if !early => {
606+
self.emit(RewriteKind::CastRefToRaw { mutbl: true });
607+
Some(Ownership::RawMut)
608+
}
609+
_ => None,
610+
},
611+
Ownership::Cell => None,
612+
Ownership::Imm => match to.own {
613+
Ownership::Raw | Ownership::RawMut if !early => {
614+
self.emit(RewriteKind::CastRefToRaw { mutbl: false });
615+
Some(Ownership::Raw)
616+
}
617+
_ => None,
618+
},
619+
Ownership::RawMut => match to.own {
620+
// For `RawMut` to `Imm`, we go through `Raw` instead of through `Mut` because
621+
// `&mut` adds more implicit constraints under the Rust memory model.
622+
Ownership::Raw | Ownership::Imm if !early => {
623+
self.emit(RewriteKind::CastRawToRaw { to_mutbl: false });
624+
Some(Ownership::Raw)
625+
}
626+
Ownership::Mut if !early => {
627+
self.emit(RewriteKind::UnsafeCastRawToRef { mutbl: true });
628+
Some(Ownership::Mut)
629+
}
630+
_ => None,
631+
},
632+
Ownership::Raw => match to.own {
633+
Ownership::RawMut | Ownership::Mut if !early => {
634+
self.emit(RewriteKind::CastRawToRaw { to_mutbl: true });
635+
Some(Ownership::RawMut)
636+
}
637+
Ownership::Imm if !early => {
638+
self.emit(RewriteKind::UnsafeCastRawToRef { mutbl: false });
639+
Some(Ownership::Imm)
640+
}
641+
_ => None,
642+
},
643+
}
644+
}
645+
503646
fn emit_cast_lty_desc(&mut self, from_lty: LTy<'tcx>, to: TypeDesc<'tcx>) {
504647
let from = type_desc::perms_to_desc_with_pointee(
505648
self.acx.tcx(),

0 commit comments

Comments
 (0)