Skip to content
Merged
4 changes: 4 additions & 0 deletions cprover_bindings/src/goto_program/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,10 @@ impl Expr {
Stmt::assign(self, rhs, loc)
}

pub fn deinit(self, loc: Location) -> Stmt {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We definitely want a comment here, especially as "deinit" is not a c notion really, and this is "cprover_bindings"

That's kind of why I thought this should be called "poison" but I don't remember if that was decided against for some reason?

Also, I don't know if this is me reading it weird, but I have the comment note that this is a shorthand/helper (perhaps by example? Like: "Construct a deinit statement from an expression by writing expr.deinit()"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it's because @celinval (correct me if I'm wrong) pointed out that "poison" in this form is only intended to model uninitialized memory. So calling it poison is too vague and deinit makes more sense. I tend to agree with that

Stmt::deinit(self, loc)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Doesn't this require that expr be an lvalue?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, but I don't know how to check for that.
Assignment checks for type equality but that's it, even though the left-hand side has to be an lvalue
image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might be nice to add an "is l-value" check to cprover bindings, but obviously not in this pr...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Annoyingly, enabling it on assign causes the stdlib test to crash. Investigating.

}

/// `if (self) { t } else { e }` or `if (self) { t }`
pub fn if_then_else(self, t: Stmt, e: Option<Stmt>, loc: Location) -> Stmt {
Stmt::if_then_else(self, t, e, loc)
Expand Down
6 changes: 6 additions & 0 deletions cprover_bindings/src/goto_program/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub enum StmtBody {
lhs: Expr, // SymbolExpr
value: Option<Expr>,
},
/// Marks the target place as uninitialized.
Comment thread
tedinski marked this conversation as resolved.
Deinit(Expr),
/// `e;`
Expression(Expr),
// `for (init; cond; update) {body}`
Expand Down Expand Up @@ -256,6 +258,10 @@ impl Stmt {
stmt!(Decl { lhs, value }, loc)
}

pub fn deinit(place: Expr, loc: Location) -> Self {
Comment thread
tedinski marked this conversation as resolved.
stmt!(Deinit(place), loc)
Comment thread
giltho marked this conversation as resolved.
}

/// `e;`
pub fn code_expression(e: Expr, loc: Location) -> Self {
stmt!(Expression(e), loc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,6 @@ pub trait Transformer: Sized {
transformed_lhs.member(field, self.symbol_table())
}

/// Transforms a CPROVER nondet call (`__nondet()`)
fn transform_expr_nondet(&mut self, typ: &Type) -> Expr {
let transformed_typ = self.transform_type(typ);
Expr::nondet(transformed_typ)
Expand Down Expand Up @@ -520,6 +519,7 @@ pub trait Transformer: Sized {
StmtBody::Break => self.transform_stmt_break(),
StmtBody::Continue => self.transform_stmt_continue(),
StmtBody::Decl { lhs, value } => self.transform_stmt_decl(lhs, value),
StmtBody::Deinit(place) => self.transform_stmt_deinit(place),
StmtBody::Expression(expr) => self.transform_stmt_expression(expr),
StmtBody::For { init, cond, update, body } => {
self.transform_stmt_for(init, cond, update, body)
Expand Down Expand Up @@ -598,6 +598,11 @@ pub trait Transformer: Sized {
Stmt::decl(transformed_lhs, transformed_value, Location::none())
}

fn transform_stmt_deinit(&mut self, place: &Expr) -> Stmt {
let transformed_place = self.transform_expr(place);
Stmt::deinit(transformed_place, Location::none())
Comment thread
tedinski marked this conversation as resolved.
}

/// Transform an expression stmt (`e;`)
fn transform_stmt_expression(&mut self, expr: &Expr) -> Stmt {
let transformed_expr = self.transform_expr(expr);
Expand Down
4 changes: 4 additions & 0 deletions cprover_bindings/src/irep/irep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ impl Irep {
}
}

pub fn with_comment<T: Into<InternedString>>(self, c: T) -> Self {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

cool. doc comment? I know I'm the one who vaguely waved you in the direction of "putting a comment on an irep" but did you dig into exactly how this works? It'd be good to explain I think...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That one is a bit blurry actually. It will add a comment directly to the irep.
As of now, comments are usually in the "location" sub, and here I'm making the decision to put it outside.
Not sure that's the best option, but the location is handled by Expr::to_irep not ExprValue::to_irep, so I can't propagate it up without a big refactoring that would clutter the entire thing for one use case.

I do think we need a more standard way of writing comments like that for alternative back-ends

self.with_named_sub(IrepId::Comment, Irep::just_string_id(c))
}

pub fn with_named_sub(mut self, key: IrepId, value: Irep) -> Self {
if !value.is_nil() {
self.named_sub.insert(key, value);
Expand Down
4 changes: 4 additions & 0 deletions cprover_bindings/src/irep/to_irep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,10 @@ impl ToIrep for StmtBody {
code_irep(IrepId::Decl, vec![lhs.to_irep(mm)])
}
}
StmtBody::Deinit(place) => {
code_irep(IrepId::Assign, vec![place.to_irep(mm), place.typ().nondet().to_irep(mm)])
Comment thread
giltho marked this conversation as resolved.
.with_comment("deinit")
}
StmtBody::Expression(e) => code_irep(IrepId::Expression, vec![e.to_irep(mm)]),
StmtBody::For { init, cond, update, body } => code_irep(
IrepId::For,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ impl<'tcx> GotocCtx<'tcx> {
}
StatementKind::Deinit(place) => {
// From rustc doc: "This writes `uninit` bytes to the entire place."
// Thus, we assign nondet() value to the entire place.
// Our model of GotoC has a similar statement, which is later lowered
// to assigning a Nondet in CBMC, with a comment specifying that it
// corresponds to a Deinit.
Comment thread
tedinski marked this conversation as resolved.
let dst_mir_ty = self.place_ty(place);
let dst_type = self.codegen_ty(dst_mir_ty);
let layout = self.layout_of(dst_mir_ty);
Expand All @@ -69,7 +71,7 @@ impl<'tcx> GotocCtx<'tcx> {
} else {
unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place(place))
.goto_expr
.assign(dst_type.nondet(), location)
.deinit(location)
}
}
StatementKind::SetDiscriminant { place, variant_index } => {
Expand Down Expand Up @@ -158,7 +160,7 @@ impl<'tcx> GotocCtx<'tcx> {
| StatementKind::Nop
| StatementKind::Coverage { .. } => Stmt::skip(location),
}
.with_location(self.codegen_span(&stmt.source_info.span))
.with_location(location)
}

/// Generate Goto-c for MIR [Terminator] statements.
Expand Down