Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

analyze: add function attrs for testing #942

Merged
merged 6 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion c2rust-analyze/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ use crate::dataflow::DataflowConstraints;
use crate::equiv::{GlobalEquivSet, LocalEquivSet};
use crate::labeled_ty::LabeledTyCtxt;
use crate::log::init_logger;
use crate::util::Callee;
use crate::panic_detail::PanicDetail;
use crate::util::{Callee, TestAttr};
use context::AdtMetadataTable;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
Expand All @@ -38,6 +39,7 @@ use rustc_span::Span;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fmt::{Debug, Display};
use std::iter;
use std::ops::{Deref, DerefMut, Index};
use std::panic::AssertUnwindSafe;

Expand Down Expand Up @@ -493,6 +495,30 @@ fn run(tcx: TyCtxt) {
info.lasn.set(lasn);
}

// For testing, putting #[c2rust_analyze_test::fixed_signature] on a function makes all
// pointers in its signature FIXED.
for &ldid in &all_fn_ldids {
if !util::has_test_attr(tcx, ldid, TestAttr::FixedSignature) {
continue;
}
let lsig = match gacx.fn_sigs.get(&ldid.to_def_id()) {
Some(x) => x,
None => continue,
};
Comment on lines +504 to +507
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spernsteiner, I vaguely remember you answering this before, but I can't find the comment. Why do we continue here instead of panicking? When would the lookup fail?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. I tried changing the continue to a panic!() just now and nothing broke in the tests (though only a few of those use fixed_signature).

make_sig_fixed(&mut gasn, lsig);
}

// For testing, putting #[c2rust_analyze_test::fail_analysis] on a function marks it as failed.
for &ldid in &all_fn_ldids {
if !util::has_test_attr(tcx, ldid, TestAttr::FailAnalysis) {
continue;
}
gacx.mark_fn_failed(
ldid.to_def_id(),
PanicDetail::new("explicit fail_analysis for testing".to_owned()),
);
}

eprintln!("=== ADT Metadata ===");
eprintln!("{:?}", gacx.adt_metadata);

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

eprintln!();

if util::has_test_attr(tcx, ldid, TestAttr::SkipRewrite) {
continue;
}

let r = panic_detail::catch_unwind(AssertUnwindSafe(|| {
let hir_body_id = tcx.hir().body_owned_by(ldid);
let expr_rewrites = rewrite::gen_expr_rewrites(&acx, &asn, &mir, hir_body_id);
Expand Down Expand Up @@ -738,6 +768,21 @@ impl<'tcx> AssignPointerIds<'tcx> for AnalysisCtxt<'_, 'tcx> {
}
}

fn make_ty_fixed(gasn: &mut GlobalAssignment, lty: LTy) {
for lty in lty.iter() {
let ptr = lty.label;
if !ptr.is_none() {
gasn.flags[ptr].insert(FlagSet::FIXED);
}
}
}

fn make_sig_fixed(gasn: &mut GlobalAssignment, lsig: &LFnSig) {
for lty in lsig.inputs.iter().copied().chain(iter::once(lsig.output)) {
make_ty_fixed(gasn, lty);
}
}

fn describe_local(tcx: TyCtxt, decl: &LocalDecl) -> String {
let mut span = decl.source_info.span;
if let Some(ref info) = decl.local_info {
Expand Down
45 changes: 44 additions & 1 deletion c2rust-analyze/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::labeled_ty::LabeledTy;
use crate::trivial::IsTrivial;
use rustc_ast::ast::AttrKind;
use rustc_const_eval::interpret::Scalar;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir::{
BasicBlock, BasicBlockData, Constant, Field, Local, Location, Mutability, Operand, Place,
PlaceElem, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind,
};
use rustc_middle::ty::{
self, AdtDef, DefIdTree, EarlyBinder, Subst, SubstsRef, Ty, TyCtxt, TyKind, UintTy,
};
use rustc_span::symbol::Symbol;
use std::fmt::Debug;

#[derive(Debug)]
Expand Down Expand Up @@ -353,3 +355,44 @@ pub fn is_null_const(constant: Constant) -> bool {

pub trait PhantomLifetime<'a> {}
impl<'a, T: ?Sized> PhantomLifetime<'a> for T {}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum TestAttr {
/// `#[c2rust_analyze_test::fixed_signature]`: Mark all pointers in the function signature as
/// [`FIXED`](crate::context::FlagSet::FIXED).
FixedSignature,
/// `#[c2rust_analyze_test::fail_analysis]`: Force an analysis failure for the function.
FailAnalysis,
/// `#[c2rust_analyze_test::skip_rewrite]`: Skip generating rewrites for the function.
SkipRewrite,
}

impl TestAttr {
pub fn name(self) -> &'static str {
match self {
TestAttr::FixedSignature => "fixed_signature",
TestAttr::FailAnalysis => "fail_analysis",
TestAttr::SkipRewrite => "skip_rewrite",
}
}
}

pub fn has_test_attr(tcx: TyCtxt, ldid: LocalDefId, attr: TestAttr) -> bool {
let tool_sym = Symbol::intern("c2rust_analyze_test");
let name_sym = Symbol::intern(attr.name());

for attr in tcx.get_attrs_unchecked(ldid.to_def_id()) {
let path = match attr.kind {
AttrKind::Normal(ref item, _) => &item.path,
AttrKind::DocComment(..) => continue,
};
let (a, b) = match &path.segments[..] {
&[ref a, ref b] => (a, b),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
&[ref a, ref b] => (a, b),
[a, b] => (a, b),

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly avoid using the "match ergonomics" feature - it makes it less clear whether the match is binding by reference or by value, which is generally useful to know IMO.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that clear from the & in the &path.segments[..]? The & and ref are basically inverses, right, so doing &[ref seems quite redundant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO &[ref a, ref b] is much more confusing than [a, b]. Match ergonomics are used all over the place, in most idiomatic Rust code and ours.

_ => continue,
};
if a.ident.name == tool_sym && b.ident.name == name_sym {
return true;
}
}
false
}
1 change: 1 addition & 0 deletions c2rust-analyze/tests/filecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ define_tests! {
offset2,
ptrptr1,
statics,
test_attrs,
trivial,
type_annotation_rewrite,
}
24 changes: 24 additions & 0 deletions c2rust-analyze/tests/filecheck/test_attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(register_tool)]
#![register_tool(c2rust_analyze_test)]

// CHECK-LABEL: type assignment for "f":
// CHECK: _0 ({{.*}}: *mut i32): *mut i32
// CHECK: _1 ({{.*}}: x): *mut i32
#[c2rust_analyze_test::fixed_signature]
fn f(x: *mut i32) -> *mut i32 {
x
}

// CHECK-LABEL: type assignment for "g":
// CHECK: _0 ({{.*}}: *mut i32): &i32
// CHECK: _1 ({{.*}}: x): &i32
#[c2rust_analyze_test::skip_rewrite]
fn g(x: *mut i32) -> *mut i32 {
x
}

// CHECK: analysis of DefId({{.*}} ~ test_attrs[{{.*}}]::h) failed: [unknown]: explicit fail_analysis for testing
#[c2rust_analyze_test::fail_analysis]
fn h(x: *mut i32) -> *mut i32 {
x
}