Skip to content

Commit 2d117b6

Browse files
nchong-at-awsdanielsn
authored andcommitted
Add vtable well-formedness flag
This is a temporary workaround for the vtable duplicate field issue (rust-lang#30). Since we can detect this case at translation time, we mark the vtable as ill-formed. This is checked at proof-time so any code that relies on using an ill-formed vtable function will fail. Co-authored-by: Daniel Schwartz-Narbonne <[email protected]>
1 parent aaa0484 commit 2d117b6

File tree

6 files changed

+107
-5
lines changed

6 files changed

+107
-5
lines changed

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/typ.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,10 +630,24 @@ impl Type {
630630
StructTag(aggr_name(name))
631631
}
632632

633+
pub fn components_are_unique(components: &Vec<DatatypeComponent>) -> bool {
634+
let mut names: Vec<_> = components.iter().map(|x| x.name()).collect();
635+
names.sort();
636+
names.dedup();
637+
names.len() == components.len()
638+
}
639+
633640
/// struct name {
634641
/// f1.typ f1.data; ...
635642
/// }
636643
pub fn struct_type(name: &str, components: Vec<DatatypeComponent>) -> Self {
644+
// TODO: turn this on after fixing issue #30
645+
// <https://github.com/model-checking/rmc/issues/30>
646+
//assert!(
647+
// Type::components_are_unique(&components),
648+
// "Components contain duplicates: {:?}",
649+
// components
650+
//);
637651
Struct { tag: name.to_string(), components }
638652
}
639653

@@ -646,6 +660,11 @@ impl Type {
646660
/// f1.typ f1.data; ...
647661
/// }
648662
pub fn union_type(name: &str, components: Vec<DatatypeComponent>) -> Self {
663+
assert!(
664+
Type::components_are_unique(&components),
665+
"Components contain duplicates: {:?}",
666+
components
667+
);
649668
Union { tag: name.to_string(), components }
650669
}
651670

compiler/rustc_codegen_llvm/src/gotoc/metadata.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ use std::iter;
2424
use std::path::Path;
2525
use tracing::debug;
2626

27+
// TODO: this is a temporary RMC-only flag used in vtables for issue #30
28+
// <https://github.com/model-checking/rmc/issues/30>
29+
pub static VTABLE_IS_WELL_FORMED_FIELD: &str = "is_vtable_well_formed";
30+
2731
// #[derive(RustcEncodable, RustcDecodable)]
2832
pub struct GotocCodegenResult {
2933
pub symtab: SymbolTable,

compiler/rustc_codegen_llvm/src/gotoc/rvalue.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,13 +884,22 @@ impl<'tcx> GotocCtx<'tcx> {
884884
Location::none(),
885885
|ctx, var| {
886886
// Build the vtable
887+
// See compiler/rustc_codegen_llvm/src/gotoc/typ.rs `trait_vtable_field_types` for field order
887888
let drop_irep = ctx.codegen_vtable_drop_in_place();
888889
let (vt_size, vt_align) = ctx.codegen_vtable_size_and_align(&src_mir_type);
889890
let mut vtable_fields = vec![drop_irep, vt_size, vt_align];
890891
let concrete_type =
891892
binders.principal().unwrap().with_self_ty(ctx.tcx, src_mir_type);
892893
let mut methods = ctx.codegen_vtable_methods(concrete_type, trait_type);
893894
vtable_fields.append(&mut methods);
895+
let fields = ctx
896+
.symbol_table
897+
.lookup_fields_in_type(&Type::struct_tag(&vtable_name))
898+
.unwrap();
899+
// TODO: this is a temporary RMC-only flag for issue #30
900+
// <https://github.com/model-checking/rmc/issues/30>
901+
let is_well_formed = Expr::c_bool_constant(Type::components_are_unique(fields));
902+
vtable_fields.push(is_well_formed);
894903
let vtable = Expr::struct_expr_from_values(
895904
Type::struct_tag(&vtable_name),
896905
vtable_fields,

compiler/rustc_codegen_llvm/src/gotoc/statement.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ impl<'tcx> GotocCtx<'tcx> {
275275
// TODO is there a better way to do this without needing the mut?
276276
let mut funce = self.codegen_operand(func);
277277

278+
let mut stmts: Vec<Stmt> = vec![];
278279
if let InstanceDef::Virtual(def_id, size) = instance.def {
279280
debug!(
280281
"Codegen a call through a virtual function. def_id: {:?} size: {:?}",
@@ -292,18 +293,29 @@ impl<'tcx> GotocCtx<'tcx> {
292293
// arg0.vtable->f(arg0.data,arg1);
293294
let vtable_ref = trait_fat_ptr.to_owned().member("vtable", &self.symbol_table);
294295
let vtable = vtable_ref.dereference();
295-
let fn_ptr = vtable.member(&vtable_field_name, &self.symbol_table);
296+
let fn_ptr = vtable.clone().member(&vtable_field_name, &self.symbol_table);
296297
funce = fn_ptr.dereference();
297298

298299
//Update the argument from arg0 to arg0.data
299300
fargs[0] = trait_fat_ptr.to_owned().member("data", &self.symbol_table);
301+
302+
// TODO: this is a temporary RMC-only flag for issue 30
303+
// <https://github.com/model-checking/rmc/issues/30>
304+
let is_well_formed = vtable
305+
.clone()
306+
.member(VTABLE_IS_WELL_FORMED_FIELD, &self.symbol_table)
307+
.cast_to(Type::bool());
308+
let assert_msg = format!("well formed vtable for type {:?}", &vtable.typ());
309+
let assert = Stmt::assert(is_well_formed, &assert_msg, loc.clone());
310+
stmts.push(assert);
300311
}
301312

302313
// Actually generate the function call, and store the return value, if any.
303-
Stmt::block(vec![
314+
stmts.append(&mut vec![
304315
self.codegen_expr_to_place(&p, funce.call(fargs)).with_location(loc.clone()),
305316
Stmt::goto(self.find_label(&target), loc),
306-
])
317+
]);
318+
Stmt::block(stmts)
307319
}
308320
ty::FnPtr(_) => {
309321
let (p, target) = destination.unwrap();

compiler/rustc_codegen_llvm/src/gotoc/typ.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
use super::cbmc::goto_program::{DatatypeComponent, Expr, Symbol, SymbolTable, Type};
44
use super::cbmc::utils::aggr_name;
5-
use super::metadata::GotocCtx;
5+
use super::metadata::{GotocCtx, VTABLE_IS_WELL_FORMED_FIELD};
66
use crate::btree_map;
77
use rustc_ast::ast::Mutability;
88
use rustc_index::vec::IndexVec;
@@ -138,6 +138,8 @@ impl<'tcx> GotocCtx<'tcx> {
138138
/// size_t align;
139139
/// int (*f)(int) f1;
140140
/// ...
141+
/// bool is_well_formed; //< TODO: this is a temporary RMC-only flag for issue #30
142+
/// // <https://github.com/model-checking/rmc/issues/30>
141143
/// }
142144
/// Ensures that the vtable is added to the symbol table.
143145
fn codegen_trait_vtable_type(&mut self, t: &'tcx ty::TyS<'tcx>) -> Type {
@@ -162,7 +164,13 @@ impl<'tcx> GotocCtx<'tcx> {
162164
})
163165
}
164166

165-
/// Given a trait of type `t`, determine the fields of the struct that will implement it's vtable.
167+
/// Given a trait of type `t`, determine the fields of the struct that will implement its vtable.
168+
///
169+
/// The order of fields (i.e., the layout of a vtable) is not guaranteed by the compiler.
170+
/// We follow the order implemented by the compiler in compiler/rustc_codegen_ssa/src/meth.rs
171+
/// `get_vtable`.
172+
///
173+
/// Currently, we also add a well-formed flag field to the end of the struct.
166174
fn trait_vtable_field_types(&mut self, t: &'tcx ty::TyS<'tcx>) -> Vec<DatatypeComponent> {
167175
if let ty::Dynamic(binder, _region) = t.kind() {
168176
// the virtual methods on the trait ref
@@ -183,6 +191,9 @@ impl<'tcx> GotocCtx<'tcx> {
183191
Type::datatype_component("align", Type::size_t()),
184192
];
185193
vtable_base.append(&mut flds);
194+
// TODO: this is a temporary RMC-only flag for issue #30
195+
// <https://github.com/model-checking/rmc/issues/30>
196+
vtable_base.push(Type::datatype_component(VTABLE_IS_WELL_FORMED_FIELD, Type::c_bool()));
186197
vtable_base
187198
} else {
188199
unreachable!("Expected to get a dynamic object here");
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
trait A {
4+
fn foo(&self) -> i32;
5+
}
6+
7+
trait B {
8+
fn foo(&self) -> i32;
9+
}
10+
11+
trait T: A + B {}
12+
13+
struct S {
14+
x: i32,
15+
y: i32,
16+
}
17+
18+
impl S {
19+
fn new(a: i32, b: i32) -> S {
20+
S { x: a, y: b }
21+
}
22+
fn new_box(a: i32, b: i32) -> Box<dyn T> {
23+
Box::new(S::new(a, b))
24+
}
25+
}
26+
27+
impl A for S {
28+
fn foo(&self) -> i32 {
29+
self.x
30+
}
31+
}
32+
33+
impl B for S {
34+
fn foo(&self) -> i32 {
35+
self.y
36+
}
37+
}
38+
39+
impl T for S {}
40+
41+
fn main() {
42+
let t = S::new_box(1, 2);
43+
let a = <dyn T as A>::foo(&*t);
44+
assert!(a == 1);
45+
let b = <dyn T as B>::foo(&*t);
46+
assert!(b == 2);
47+
}

0 commit comments

Comments
 (0)