Skip to content

Commit a10fe87

Browse files
avanhatttedinski
authored andcommitted
Fix for duplicate vtable field names (rust-lang#188)
Handle duplicate dynamic trait names correctly, for now keeping the string-matching based vtable implementation. In the generated vtable itself, use the function name scoped to the specific trait (A::foo vs B::foo) instead of just the function name. Merge some vtable lookup handling. Add tests for more potential naming conflicts.
1 parent 0a9f612 commit a10fe87

File tree

10 files changed

+244
-92
lines changed

10 files changed

+244
-92
lines changed

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -653,13 +653,11 @@ impl Type {
653653
/// f1.typ f1.data; ...
654654
/// }
655655
pub fn struct_type(name: &str, components: Vec<DatatypeComponent>) -> Self {
656-
// TODO: turn this on after fixing issue #30
657-
// <https://github.com/model-checking/rmc/issues/30>
658-
//assert!(
659-
// Type::components_are_unique(&components),
660-
// "Components contain duplicates: {:?}",
661-
// components
662-
//);
656+
assert!(
657+
Type::components_are_unique(&components),
658+
"Components contain duplicates: {:?}",
659+
components
660+
);
663661
Struct { tag: name.to_string(), components }
664662
}
665663

compiler/rustc_codegen_llvm/src/gotoc/metadata.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@ use rustc_middle::mir::interpret::Allocation;
1717
use rustc_middle::mir::{BasicBlock, Body, HasLocalDecls, Local, Operand, Place, Rvalue};
1818
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, TyAndLayout};
1919
use rustc_middle::ty::print::with_no_trimmed_paths;
20-
use rustc_middle::ty::{self, Binder, Instance, TraitRef, Ty, TyCtxt, TypeFoldable};
20+
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
2121
use rustc_target::abi::{HasDataLayout, LayoutOf, TargetDataLayout};
2222
use rustc_target::spec::Target;
2323
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-
3127
// #[derive(RustcEncodable, RustcDecodable)]
3228
pub struct GotocCodegenResult {
3329
pub symtab: SymbolTable,
@@ -188,8 +184,7 @@ impl<'tcx> GotocCtx<'tcx> {
188184

189185
/// Pretty name including crate path and trait information. For example:
190186
/// boxtrait_fail::<Concrete as Trait>::increment
191-
/// Generated from the fn instance to insert _into_ the symbol table.
192-
/// Must match the format of pretty_name_from_dynamic_object.
187+
/// Generated from the fn instance to insert into/lookup in the symbol table.
193188
/// TODO: internal unit tests https://github.com/model-checking/rmc/issues/172
194189
pub fn pretty_name_from_instance(&self, instance: Instance<'tcx>) -> String {
195190
format!(
@@ -199,22 +194,17 @@ impl<'tcx> GotocCtx<'tcx> {
199194
)
200195
}
201196

202-
/// Pretty name including crate path and trait information. For example:
203-
/// boxtrait_fail::<Concrete as Trait>::increment
204-
/// Generated from the dynamic object type for _lookup_ from the symbol table.
205-
/// Must match the format of pretty_name_from_instance.
206-
pub fn pretty_name_from_dynamic_object(
207-
&self,
208-
def_id: DefId,
209-
trait_ref_t: Binder<'_, TraitRef<'tcx>>,
210-
) -> String {
211-
let normalized_object_type_name = self.normalized_name_of_dynamic_object_type(trait_ref_t);
212-
format!(
213-
"{}::{}::{}",
214-
self.tcx.crate_name(def_id.krate),
215-
normalized_object_type_name,
216-
self.tcx.item_name(def_id)
217-
)
197+
/// For the vtable field name, we need exactly the dyn trait name and the function
198+
/// name. The table itself already is scoped by the object type.
199+
/// Example: ::Shape::vol
200+
/// Note: this is _not_ the same name for top-level entry into the symbol table,
201+
/// which does need more crate and type information. For now, the symbol table
202+
/// name is from the pretty_name_from_instance function above.
203+
pub fn vtable_field_name(&self, def_id: DefId) -> String {
204+
// `to_string_no_crate_verbose` is from Rust proper, we use it here because it
205+
// always includes the dyn trait name and function name.
206+
// Tracking a less brittle solution here: https://github.com/model-checking/rmc/issues/187
207+
self.tcx.def_path(def_id).to_string_no_crate_verbose()
218208
}
219209

220210
/// a human readable name in rust for reference

compiler/rustc_codegen_llvm/src/gotoc/rvalue.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::utils::{dynamic_fat_ptr, slice_fat_ptr};
99
use crate::btree_string_map;
1010
use rustc_middle::mir::{AggregateKind, BinOp, CastKind, NullOp, Operand, Place, Rvalue, UnOp};
1111
use rustc_middle::ty::adjustment::PointerCast;
12-
use rustc_middle::ty::{self, Binder, IntTy, TraitRef, Ty, UintTy};
12+
use rustc_middle::ty::{self, Binder, Instance, IntTy, TraitRef, Ty, UintTy};
1313
use rustc_span::def_id::DefId;
1414
use rustc_target::abi::{FieldsShape, LayoutOf, Primitive, TagEncoding, Variants};
1515
use tracing::{debug, warn};
@@ -671,15 +671,29 @@ impl<'tcx> GotocCtx<'tcx> {
671671
trait_ref_t: Binder<'_, TraitRef<'tcx>>,
672672
t: Ty<'tcx>,
673673
) -> Expr {
674-
let function_name = self.tcx.item_name(def_id).to_string();
674+
let vtable_field_name = self.vtable_field_name(def_id);
675675
let vtable_type_name = aggr_name(&self.vtable_name(t));
676676
let field_type = self
677677
.symbol_table
678-
.lookup_field_type(&vtable_type_name, &function_name)
678+
.lookup_field_type(&vtable_type_name, &vtable_field_name)
679679
.cloned()
680680
.unwrap();
681681

682-
let pretty_function_name = self.pretty_name_from_dynamic_object(def_id, trait_ref_t);
682+
// We use Instance::resolve to more closely match Rust proper behavior. The comment
683+
// there says "used to find the precise code that will run for a trait method invocation"
684+
// and it is used (in a more indirect way) to generate vtables.
685+
let instance = Instance::resolve(
686+
self.tcx,
687+
ty::ParamEnv::reveal_all(),
688+
def_id,
689+
trait_ref_t.skip_binder().substs,
690+
)
691+
.unwrap()
692+
.unwrap();
693+
694+
// TODO: stop using this pretty name here
695+
// https://github.com/model-checking/rmc/issues/187
696+
let pretty_function_name = self.pretty_name_from_instance(instance);
683697
let matching_symbols = self.symbol_table.find_by_pretty_name(&pretty_function_name); //("<path>::<Rectangle as Vol>::vol");
684698
match matching_symbols.len() {
685699
0 => {
@@ -809,14 +823,6 @@ impl<'tcx> GotocCtx<'tcx> {
809823
binders.principal().unwrap().with_self_ty(ctx.tcx, src_mir_type);
810824
let mut methods = ctx.codegen_vtable_methods(concrete_type, trait_type);
811825
vtable_fields.append(&mut methods);
812-
let fields = ctx
813-
.symbol_table
814-
.lookup_fields_in_type(&Type::struct_tag(&vtable_name))
815-
.unwrap();
816-
// TODO: this is a temporary RMC-only flag for issue #30
817-
// <https://github.com/model-checking/rmc/issues/30>
818-
let is_well_formed = Expr::c_bool_constant(Type::components_are_unique(fields));
819-
vtable_fields.push(is_well_formed);
820826
let vtable = Expr::struct_expr_from_values(
821827
Type::struct_tag(&vtable_name),
822828
vtable_fields,

compiler/rustc_codegen_llvm/src/gotoc/statement.rs

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -281,48 +281,39 @@ impl<'tcx> GotocCtx<'tcx> {
281281
// Mutable so that we can override it in case of a virtual function call
282282
// TODO is there a better way to do this without needing the mut?
283283
let mut funce = self.codegen_operand(func);
284-
285-
let mut stmts: Vec<Stmt> = vec![];
286284
if let InstanceDef::Virtual(def_id, size) = instance.def {
287285
debug!(
288286
"Codegen a call through a virtual function. def_id: {:?} size: {:?}",
289287
def_id, size
290288
);
291289

292-
//The first argument to a virtual function is a fat pointer for the trait
290+
// The first argument to a virtual function is a fat pointer for the trait
293291
let trait_fat_ptr = fargs[0].to_owned();
294-
let vtable_field_name = self.tcx.item_name(def_id).to_string();
292+
let vtable_field_name = self.vtable_field_name(def_id);
295293

296-
//now that we have all the stuff we need, we can actually build the dynamic call
294+
// Now that we have all the stuff we need, we can actually build the dynamic call
297295
// If the original call was of the form
298296
// f(arg0, arg1);
299297
// The new call should be of the form
300298
// arg0.vtable->f(arg0.data,arg1);
301299
let vtable_ref = trait_fat_ptr.to_owned().member("vtable", &self.symbol_table);
302300
let vtable = vtable_ref.dereference();
303-
let fn_ptr = vtable.clone().member(&vtable_field_name, &self.symbol_table);
301+
let fn_ptr = vtable.member(&vtable_field_name, &self.symbol_table);
304302
funce = fn_ptr.dereference();
305303

306304
//Update the argument from arg0 to arg0.data
307305
fargs[0] = trait_fat_ptr.to_owned().member("data", &self.symbol_table);
308-
309-
// TODO: this is a temporary RMC-only flag for issue 30
310-
// <https://github.com/model-checking/rmc/issues/30>
311-
let is_well_formed = vtable
312-
.clone()
313-
.member(VTABLE_IS_WELL_FORMED_FIELD, &self.symbol_table)
314-
.cast_to(Type::bool());
315-
let assert_msg = format!("well formed vtable for type {:?}", &vtable.typ());
316-
let assert = Stmt::assert(is_well_formed, &assert_msg, loc.clone());
317-
stmts.push(assert);
318306
}
319307

320308
// Actually generate the function call, and store the return value, if any.
321-
stmts.append(&mut vec![
322-
self.codegen_expr_to_place(&p, funce.call(fargs)).with_location(loc.clone()),
323-
Stmt::goto(self.find_label(&target), loc.clone()),
324-
]);
325-
Stmt::block(stmts, loc)
309+
Stmt::block(
310+
vec![
311+
self.codegen_expr_to_place(&p, funce.call(fargs))
312+
.with_location(loc.clone()),
313+
Stmt::goto(self.find_label(&target), loc.clone()),
314+
],
315+
loc,
316+
)
326317
}
327318
ty::FnPtr(_) => {
328319
let (p, target) = destination.unwrap();

compiler/rustc_codegen_llvm/src/gotoc/typ.rs

Lines changed: 5 additions & 29 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, VTABLE_IS_WELL_FORMED_FIELD};
5+
use super::metadata::GotocCtx;
66
use crate::btree_map;
77
use rustc_ast::ast::Mutability;
88
use rustc_index::vec::IndexVec;
@@ -11,8 +11,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
1111
use rustc_middle::ty::print::FmtPrinter;
1212
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
1313
use rustc_middle::ty::{
14-
self, AdtDef, Binder, FloatTy, Instance, IntTy, PolyFnSig, TraitRef, Ty, TyS, UintTy,
15-
VariantDef,
14+
self, AdtDef, FloatTy, Instance, IntTy, PolyFnSig, Ty, TyS, UintTy, VariantDef,
1615
};
1716
use rustc_span;
1817
use rustc_span::def_id::DefId;
@@ -131,14 +130,13 @@ impl<'tcx> GotocCtx<'tcx> {
131130
// gives an Irep Pointer object for the signature
132131
let fnptr = self.codegen_dynamic_function_sig(sig).to_pointer();
133132

134-
//DSN For now, use the pretty name not the mangled name.
135-
let _mangled_fname = self.symbol_name(instance);
136-
let pretty_fname = self.tcx.item_name(def_id).to_string();
133+
// vtable field name, i.e., ::Shape::vol
134+
let vtable_field_name = self.vtable_field_name(def_id);
137135

138136
let ins_ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
139137
let _layout = self.layout_of(ins_ty);
140138

141-
Type::datatype_component(&pretty_fname, fnptr)
139+
Type::datatype_component(&vtable_field_name, fnptr)
142140
}
143141

144142
/// Generates a vtable that looks like this:
@@ -148,8 +146,6 @@ impl<'tcx> GotocCtx<'tcx> {
148146
/// size_t align;
149147
/// int (*f)(int) f1;
150148
/// ...
151-
/// bool is_well_formed; //< TODO: this is a temporary RMC-only flag for issue #30
152-
/// // <https://github.com/model-checking/rmc/issues/30>
153149
/// }
154150
/// Ensures that the vtable is added to the symbol table.
155151
fn codegen_trait_vtable_type(&mut self, t: &'tcx ty::TyS<'tcx>) -> Type {
@@ -201,32 +197,12 @@ impl<'tcx> GotocCtx<'tcx> {
201197
Type::datatype_component("align", Type::size_t()),
202198
];
203199
vtable_base.append(&mut flds);
204-
// TODO: this is a temporary RMC-only flag for issue #30
205-
// <https://github.com/model-checking/rmc/issues/30>
206-
vtable_base.push(Type::datatype_component(VTABLE_IS_WELL_FORMED_FIELD, Type::c_bool()));
207200
vtable_base
208201
} else {
209202
unreachable!("Expected to get a dynamic object here");
210203
}
211204
}
212205

213-
/// Given a Binder<TraitRef>, gives back a normalized name for the dynamic type
214-
/// For example, if we have a `Rectangle` that implements a `Shape`, this will give back
215-
/// "<Rectangle as Shape>"
216-
///
217-
/// This is used to generate the pretty name of trait methods when building the vtable.
218-
/// Ideally, we would just use Instance::resolve() to get a defid for a vtable method.
219-
/// Unfortunately, this doesn't currently work, so instead, we look at the pretty name of the method, and look by that.
220-
/// As with vtable_name, we have both cases which have "&" and cases which don't.
221-
/// e.g. "<Rectangle as Shape>" and "<&Rectangle as Shape>".
222-
/// We solve this by normalizeing and removing the "&">::vol", but the inner type would be <&Rectangle as Vol>
223-
pub fn normalized_name_of_dynamic_object_type(
224-
&self,
225-
trait_ref_t: Binder<'_, TraitRef<'tcx>>,
226-
) -> String {
227-
with_no_trimmed_paths(|| trait_ref_t.skip_binder().to_string().replace("&", ""))
228-
}
229-
230206
/// Gives the name for a trait.
231207
/// In some cases, we have &T, in other cases T, so normalize.
232208
pub fn normalized_trait_name(&self, t: Ty<'tcx>) -> String {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// This test uses a trait defined in a different crate (the standard library)
5+
// and a test defined in the local crate. The goal is to test vtable resolution
6+
// of duplicate names across different crates.
7+
struct Counter {
8+
count: usize,
9+
}
10+
11+
// A custom impl for the standard library trait.
12+
impl std::iter::Iterator for Counter {
13+
type Item = usize;
14+
15+
fn next(&mut self) -> Option<Self::Item> {
16+
// Increment our count.
17+
self.count += 1;
18+
Some(self.count)
19+
}
20+
}
21+
22+
// An impl for our local trait, with an indentical name to the standard library
23+
trait Iterator {
24+
fn next(&mut self) -> Option<usize>;
25+
}
26+
27+
impl Iterator for Counter {
28+
fn next(&mut self) -> Option<usize> {
29+
Some(42)
30+
}
31+
}
32+
33+
trait Combined : Iterator + std::iter::Iterator<Item = usize> {}
34+
35+
impl Combined for Counter {}
36+
37+
fn std_count(c : &mut dyn std::iter::Iterator<Item = usize>) -> usize {
38+
c.next().unwrap()
39+
}
40+
41+
fn weird_count(c : &mut dyn Iterator) -> usize {
42+
c.next().unwrap()
43+
}
44+
45+
fn main() {
46+
let counter : &mut Counter = &mut Counter { count: 0 };
47+
assert!(std_count(counter as &mut dyn std::iter::Iterator<Item = usize>) == 1);
48+
assert!(weird_count(counter as &mut dyn Iterator) == 42);
49+
50+
let counter_combined = counter as &mut dyn Combined;
51+
assert!(std::iter::Iterator::next(counter_combined).unwrap() == 2);
52+
assert!(Iterator::next(counter_combined).unwrap() == 42);
53+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// This test uses a trait defined in a different crate (the standard library)
5+
// and a test defined in the local crate. The goal is to test vtable resolution
6+
// of duplicate names across different crates.
7+
struct Counter {
8+
count: usize,
9+
}
10+
11+
// A custom impl for the standard library trait.
12+
impl std::iter::Iterator for Counter {
13+
type Item = usize;
14+
15+
fn next(&mut self) -> Option<Self::Item> {
16+
// Increment our count.
17+
self.count += 1;
18+
Some(self.count)
19+
}
20+
}
21+
22+
// An impl for our local trait, with an indentical name to the standard library
23+
trait Iterator {
24+
fn next(&mut self) -> Option<usize>;
25+
}
26+
27+
impl Iterator for Counter {
28+
fn next(&mut self) -> Option<usize> {
29+
Some(42)
30+
}
31+
}
32+
33+
trait Combined : Iterator + std::iter::Iterator<Item = usize> {}
34+
35+
impl Combined for Counter {}
36+
37+
fn std_count(c : &mut dyn std::iter::Iterator<Item = usize>) -> usize {
38+
c.next().unwrap()
39+
}
40+
41+
fn weird_count(c : &mut dyn Iterator) -> usize {
42+
c.next().unwrap()
43+
}
44+
45+
fn main() {
46+
let counter : &mut Counter = &mut Counter { count: 0 };
47+
assert!(std_count(counter as &mut dyn std::iter::Iterator<Item = usize>) == 0); // Should be 1
48+
assert!(weird_count(counter as &mut dyn Iterator) == 0); // Should be 42
49+
50+
let counter_combined = counter as &mut dyn Combined;
51+
assert!(std::iter::Iterator::next(counter_combined).unwrap() == 0); // Should be 2
52+
assert!(Iterator::next(counter_combined).unwrap() == 0); // Should be 42
53+
}

0 commit comments

Comments
 (0)