Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

debuginfo: Generate cross-crate unique type identifiers for debuginfo…

… types.

With this change, rustc creates a unique type identifier for types in debuginfo. These type identifiers are used by LLVM to correctly handle link-time-optimization scenarios but also help rustc with dealing with inlining from other crates. For more information, see the documentation block at the top of librustc/middle/trans/debuginfo.rs.

Fixes #13681.
  • Loading branch information...
commit 0a98a4e422bd2d80ee7910b00c0286f1eefde9c5 1 parent c0a6f72
@michaelwoerister michaelwoerister authored
View
795 src/librustc/middle/trans/debuginfo.rs
@@ -122,9 +122,37 @@ prologue detection. The `create_argument_metadata()` and related functions take
`llvm.dbg.declare` instructions to the correct source locations even while source location emission
is still disabled, so there is no need to do anything special with source location handling here.
+## Unique Type Identification
+In order for link-time optimization to work properly, LLVM needs a unique type identifier that tells
+it across compilation units which types are the same as others. This type identifier is created by
+TypeMap::get_unique_type_id_of_type() using the following algorithm:
+
+(1) Primitive types have their name as ID
+(2) Structs, enums and traits have a multipart identifier
+ (1) The first part is the SVH (strict version hash) of the crate they were originally defined in
+ (2) The second part is the ast::NodeId of the definition in their original crate
+ (3) The final part is a concatenation of the type IDs of their concrete type arguments if they
+ are generic types.
+(3) Tuple-, pointer and function types are structurally identified, which means that they are
+ equivalent if their component types are equivalent (i.e. (int, int) is the same regardless in
+ which crate it is used).
+
+This algorithm also provides a stable ID for types that are defined in one crate but instantiated
+from metadata within another crate. We just have to take care to always map crate and node IDs back
+to the original crate context.
+
+As a side-effect these unique type IDs also help to solve a problem arising from lifetime
+parameters. Since lifetime parameters are completely omitted in debuginfo, more than one `ty::t`
+instance may map to the same debuginfo type metadata, that is, some struct `Struct<'a>` may have N
+instantiations with different concrete substitutions for `'a`, and thus there will be N `ty::t`
+instances for the type `Struct<'a>` even though it is not generic otherwise. Unfortunately this
+means that we cannot use `ty::type_id()` as cheap identifier for type metadata---we have done this
+in the past, but it led to unnecessary metadata duplication in the best case and LLVM assertions in
+the worst. However, the unique type ID as described above *can* be used as identifier. Since it is
+comparatively expensive to construct, though, `ty::type_id()` is still used additionally as an
+optimization for cases where the exact same type has been seen before (which is most of the time).
*/
-
use driver::config;
use driver::config::{FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
use lib::llvm::llvm;
@@ -152,6 +180,7 @@ use std::gc::Gc;
use std::ptr;
use std::rc::{Rc, Weak};
use std::sync::atomics;
+use syntax::util::interner::Interner;
use syntax::codemap::{Span, Pos};
use syntax::{abi, ast, codemap, ast_util, ast_map};
use syntax::owned_slice::OwnedSlice;
@@ -166,7 +195,6 @@ static DW_TAG_arg_variable: c_uint = 0x101;
static DW_ATE_boolean: c_uint = 0x02;
static DW_ATE_float: c_uint = 0x04;
static DW_ATE_signed: c_uint = 0x05;
-// static DW_ATE_signed_char: c_uint = 0x06;
static DW_ATE_unsigned: c_uint = 0x07;
static DW_ATE_unsigned_char: c_uint = 0x08;
@@ -174,15 +202,370 @@ static DW_ATE_unsigned_char: c_uint = 0x08;
// Public Interface of debuginfo module
//=-------------------------------------------------------------------------------------------------
+#[deriving(Copy, Show, Hash, Eq, PartialEq, Clone)]
+struct UniqueTypeId(ast::Name);
+
+// The TypeMap is where the CrateDebugContext holds the type metadata nodes created so far. The
+// metadata nodes are indexed by UniqueTypeId, and, for faster lookup, also by ty::t. The
+// TypeMap is responsible for creating UniqueTypeIds.
+struct TypeMap {
+ // The UniqueTypeIds created so far
+ unique_id_interner: Interner<Rc<String>>,
+ // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping.
+ unique_id_to_metadata: HashMap<UniqueTypeId, DIType>,
+ // A map from ty::type_id() to debuginfo metadata. This is a N:1 mapping.
+ type_to_metadata: HashMap<uint, DIType>,
+ // A map from ty::type_id() to UniqueTypeId. This is a N:1 mapping.
+ type_to_unique_id: HashMap<uint, UniqueTypeId>
+}
+
+impl TypeMap {
+
+ fn new() -> TypeMap {
+ TypeMap {
+ unique_id_interner: Interner::new(),
+ type_to_metadata: HashMap::new(),
+ unique_id_to_metadata: HashMap::new(),
+ type_to_unique_id: HashMap::new(),
+ }
+ }
+
+ // Adds a ty::t to metadata mapping to the TypeMap. The method will fail if the mapping already
+ // exists.
+ fn register_type_with_metadata(&mut self,
+ cx: &CrateContext,
+ type_: ty::t,
+ metadata: DIType) {
+ if !self.type_to_metadata.insert(ty::type_id(type_), metadata) {
+ cx.sess().bug(format!("Type metadata for ty::t '{}' is already in the TypeMap!",
+ ppaux::ty_to_str(cx.tcx(), type_)).as_slice());
+ }
+ }
+
+ // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will fail if the mapping
+ // already exists.
+ fn register_unique_id_with_metadata(&mut self,
+ cx: &CrateContext,
+ unique_type_id: UniqueTypeId,
+ metadata: DIType) {
+ if !self.unique_id_to_metadata.insert(unique_type_id, metadata) {
+ let unique_type_id_str = self.get_unique_type_id_as_string(unique_type_id);
+ cx.sess().bug(format!("Type metadata for unique id '{}' is already in the TypeMap!",
+ unique_type_id_str.as_slice()).as_slice());
+ }
+ }
+
+ fn find_metadata_for_type(&self, type_: ty::t) -> Option<DIType> {
+ self.type_to_metadata.find_copy(&ty::type_id(type_))
+ }
+
+ fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option<DIType> {
+ self.unique_id_to_metadata.find_copy(&unique_type_id)
+ }
+
+ // Get the string representation of a UniqueTypeId. This method will fail if the id is unknown.
+ fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> Rc<String> {
+ let UniqueTypeId(interner_key) = unique_type_id;
+ self.unique_id_interner.get(interner_key)
+ }
+
+ // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given type has been
+ // requested before, this is just a table lookup. Otherwise an ID will be generated and stored
+ // for later lookup.
+ fn get_unique_type_id_of_type(&mut self, cx: &CrateContext, type_: ty::t) -> UniqueTypeId {
+
+ // basic type -> {:name of the type:}
+ // tuple -> {tuple_(:param-uid:)*}
+ // struct -> {struct_:svh: / :node-id:_<(:param-uid:),*> }
+ // enum -> {enum_:svh: / :node-id:_<(:param-uid:),*> }
+ // enum variant -> {variant_:variant-name:_:enum-uid:}
+ // reference (&) -> {& :pointee-uid:}
+ // mut reference (&mut) -> {&mut :pointee-uid:}
+ // ptr (*) -> {* :pointee-uid:}
+ // mut ptr (*mut) -> {*mut :pointee-uid:}
+ // unique ptr (~) -> {~ :pointee-uid:}
+ // @-ptr (@) -> {@ :pointee-uid:}
+ // sized vec ([T, ..x]) -> {[:size:] :element-uid:}
+ // vec slice (&[T]) -> {&<mut> [] :element-uid:}
+ // trait (~ | &[mut] T) -> {:sigil: trait_:svh: / :node-id:_<(:param-uid:),*> }
+ // closure -> {<unsafe_> <once_> :store-sigil: |(:param-uid:),* <,_...>| -> \
+ // :return-type-uid: : (:bounds:)*}
+ // function -> {<unsafe_> <abi_> fn( (:param-uid:)* <,_...> ) -> \
+ // :return-type-uid:}
+
+ match self.type_to_unique_id.find_copy(&ty::type_id(type_)) {
+ Some(unique_type_id) => return unique_type_id,
+ None => { /* generate one */}
+ };
+
+ let mut unique_type_id = String::with_capacity(256);
+ unique_type_id.push_char('{');
+
+ match ty::get(type_).sty {
+ ty::ty_nil |
+ ty::ty_bot |
+ ty::ty_bool |
+ ty::ty_char |
+ ty::ty_str |
+ ty::ty_int(_) |
+ ty::ty_uint(_) |
+ ty::ty_float(_) => {
+ unique_type_id.push_str(ppaux::ty_to_str(cx.tcx(), type_).as_slice());
+ },
+ ty::ty_enum(def_id, ref substs) => {
+ unique_type_id.push_str("enum ");
+ from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id);
+ },
+ ty::ty_struct(def_id, ref substs) => {
+ unique_type_id.push_str("struct ");
+ from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id);
+ },
+ ty::ty_tup(ref component_types) => {
+ unique_type_id.push_str("tuple ");
+ for &component_type in component_types.iter() {
+ let component_type_id = self.get_unique_type_id_of_type(cx, component_type);
+ let component_type_id = self.get_unique_type_id_as_string(component_type_id);
+ unique_type_id.push_str(component_type_id.as_slice());
+ }
+ },
+ ty::ty_box(inner_type) => {
+ unique_type_id.push_char('@');
+ let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type);
+ let inner_type_id = self.get_unique_type_id_as_string(inner_type_id);
+ unique_type_id.push_str(inner_type_id.as_slice());
+ },
+ ty::ty_uniq(inner_type) => {
+ unique_type_id.push_char('~');
+ let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type);
+ let inner_type_id = self.get_unique_type_id_as_string(inner_type_id);
+ unique_type_id.push_str(inner_type_id.as_slice());
+ },
+ ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => {
+ unique_type_id.push_char('*');
+ if mutbl == ast::MutMutable {
+ unique_type_id.push_str("mut");
+ }
+
+ let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type);
+ let inner_type_id = self.get_unique_type_id_as_string(inner_type_id);
+ unique_type_id.push_str(inner_type_id.as_slice());
+ },
+ ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => {
+ unique_type_id.push_char('&');
+ if mutbl == ast::MutMutable {
+ unique_type_id.push_str("mut");
+ }
+
+ let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type);
+ let inner_type_id = self.get_unique_type_id_as_string(inner_type_id);
+ unique_type_id.push_str(inner_type_id.as_slice());
+ },
+ ty::ty_vec(ty::mt { ty: inner_type, mutbl }, optional_length) => {
+ match optional_length {
+ Some(len) => {
+ unique_type_id.push_str(format!("[{}]", len).as_slice());
+ }
+ None => {
+ unique_type_id.push_char('&');
+
+ if mutbl == ast::MutMutable {
+ unique_type_id.push_str("mut");
+ }
+
+ unique_type_id.push_str("[]");
+ }
+ };
+
+ let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type);
+ let inner_type_id = self.get_unique_type_id_as_string(inner_type_id);
+ unique_type_id.push_str(inner_type_id.as_slice());
+ },
+ ty::ty_trait(ref trait_data) => {
+ match trait_data.store {
+ ty::UniqTraitStore => unique_type_id.push_char('~'),
+ ty::RegionTraitStore(_, ast::MutMutable) => unique_type_id.push_str("&mut"),
+ ty::RegionTraitStore(_, ast::MutImmutable) => unique_type_id.push_char('&'),
+ };
+
+ unique_type_id.push_str("trait ");
+
+ from_def_id_and_substs(self,
+ cx,
+ trait_data.def_id,
+ &trait_data.substs,
+ &mut unique_type_id);
+ },
+ ty::ty_bare_fn(ty::BareFnTy{ fn_style, abi, ref sig } ) => {
+ if fn_style == ast::UnsafeFn {
+ unique_type_id.push_str("unsafe ");
+ }
+
+ unique_type_id.push_str(abi.name());
+
+ unique_type_id.push_str(" fn(");
+
+ for &parameter_type in sig.inputs.iter() {
+ let parameter_type_id = self.get_unique_type_id_of_type(cx, parameter_type);
+ let parameter_type_id = self.get_unique_type_id_as_string(parameter_type_id);
+ unique_type_id.push_str(parameter_type_id.as_slice());
+ unique_type_id.push_char(',');
+ }
+
+ if sig.variadic {
+ unique_type_id.push_str("...");
+ }
+
+ unique_type_id.push_str(")->");
+ let return_type_id = self.get_unique_type_id_of_type(cx, sig.output);
+ let return_type_id = self.get_unique_type_id_as_string(return_type_id);
+ unique_type_id.push_str(return_type_id.as_slice());
+ },
+ ty::ty_closure(box ty::ClosureTy { fn_style,
+ onceness,
+ store,
+ ref bounds,
+ ref sig }) => {
+ if fn_style == ast::UnsafeFn {
+ unique_type_id.push_str("unsafe ");
+ }
+
+ if onceness == ast::Once {
+ unique_type_id.push_str("once ");
+ }
+
+ match store {
+ ty::UniqTraitStore => unique_type_id.push_str("~|"),
+ ty::RegionTraitStore(_, ast::MutMutable) => unique_type_id.push_str("&mut|"),
+ ty::RegionTraitStore(_, ast::MutImmutable) => unique_type_id.push_str("&|"),
+ };
+
+ for &parameter_type in sig.inputs.iter() {
+ let parameter_type_id = self.get_unique_type_id_of_type(cx, parameter_type);
+ let parameter_type_id = self.get_unique_type_id_as_string(parameter_type_id);
+ unique_type_id.push_str(parameter_type_id.as_slice());
+ unique_type_id.push_char(',');
+ }
+
+ if sig.variadic {
+ unique_type_id.push_str("...");
+ }
+
+ unique_type_id.push_str("|->");
+
+ let return_type_id = self.get_unique_type_id_of_type(cx, sig.output);
+ let return_type_id = self.get_unique_type_id_as_string(return_type_id);
+ unique_type_id.push_str(return_type_id.as_slice());
+
+ unique_type_id.push_char(':');
+
+ for bound in bounds.iter() {
+ match bound {
+ ty::BoundStatic => unique_type_id.push_str("'static"),
+ ty::BoundSend => unique_type_id.push_str("Send"),
+ ty::BoundSized => unique_type_id.push_str("Sized"),
+ ty::BoundCopy => unique_type_id.push_str("Copy"),
+ ty::BoundShare => unique_type_id.push_str("Share"),
+ };
+ unique_type_id.push_char('+');
+ }
+ },
+ _ => {
+ cx.sess().bug(format!("get_unique_type_id_of_type() - unexpected type: {}, {:?}",
+ ppaux::ty_to_str(cx.tcx(), type_).as_slice(),
+ ty::get(type_).sty).as_slice())
+ }
+ };
+
+ unique_type_id.push_char('}');
+
+ // Trim to size before storing permanently
+ unique_type_id.shrink_to_fit();
+
+ let key = self.unique_id_interner.intern(Rc::new(unique_type_id));
+ self.type_to_unique_id.insert(ty::type_id(type_), UniqueTypeId(key));
+
+ return UniqueTypeId(key);
+
+ fn from_def_id_and_substs(type_map: &mut TypeMap,
+ cx: &CrateContext,
+ def_id: ast::DefId,
+ substs: &subst::Substs,
+ output: &mut String) {
+ use std::num::ToStrRadix;
+
+ // First, find out the 'real' def_id of the type. Items inlined from other crates have
+ // to be mapped back to their source.
+ let source_def_id = if def_id.krate == ast::LOCAL_CRATE {
+ match cx.external_srcs.borrow().find_copy(&def_id.node) {
+ Some(source_def_id) => {
+ // The given def_id identifies the inlined copy of a type definition,
+ // let's take the source of the copy
+ source_def_id
+ }
+ None => def_id
+ }
+ } else {
+ def_id
+ };
+
+ // Get the crate hash as first part of the identifier
+ let crate_hash = if source_def_id.krate == ast::LOCAL_CRATE {
+ cx.link_meta.crate_hash.clone()
+ } else {
+ cx.sess().cstore.get_crate_hash(source_def_id.krate)
+ };
+
+ output.push_str(crate_hash.as_str());
+ output.push_str("/");
+ output.push_str(def_id.node.to_str_radix(16).as_slice());
+
+ // Maybe check that there is no self type here
+
+ if substs.tps.len() > 0 {
+ output.push_char('<');
+
+ for &type_parameter in substs.tps.iter() {
+ let param_type_id = type_map.get_unique_type_id_of_type(cx, type_parameter);
+ let param_type_id = type_map.get_unique_type_id_as_string(param_type_id);
+ output.push_str(param_type_id.as_slice());
+ output.push_char(',');
+ }
+
+ output.push_char('>');
+ }
+ }
+ }
+
+ // Get the UniqueTypeId for an enum variant. Enum variants are not really types of their own,
+ // so they need special handling. We still need a UniqueTypeId for them, since to debuginfo they
+ // *are* real types.
+ fn get_unique_type_id_of_enum_variant(&mut self,
+ cx: &CrateContext,
+ enum_type: ty::t,
+ variant_name: &str)
+ -> UniqueTypeId {
+ let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type);
+ let enum_variant_type_id = format!("{}::{}",
+ self.get_unique_type_id_as_string(enum_type_id)
+ .as_slice(),
+ variant_name);
+ let interner_key = self.unique_id_interner.intern(Rc::new(enum_variant_type_id));
+ UniqueTypeId(interner_key)
+ }
+}
+
+
/// A context object for maintaining all state needed by the debuginfo module.
pub struct CrateDebugContext {
llcontext: ContextRef,
builder: DIBuilderRef,
current_debug_location: Cell<DebugLocation>,
created_files: RefCell<HashMap<String, DIFile>>,
- created_types: RefCell<HashMap<uint, DIType>>,
created_enum_disr_types: RefCell<HashMap<ast::DefId, DIType>>,
+
+ type_map: RefCell<TypeMap>,
namespace_map: RefCell<HashMap<Vec<ast::Name>, Rc<NamespaceTreeNode>>>,
+
// This collection is used to assert that composite types (structs, enums, ...) have their
// members only set once:
composite_types_completed: RefCell<HashSet<DIType>>,
@@ -199,8 +582,8 @@ impl CrateDebugContext {
builder: builder,
current_debug_location: Cell::new(UnknownLocation),
created_files: RefCell::new(HashMap::new()),
- created_types: RefCell::new(HashMap::new()),
created_enum_disr_types: RefCell::new(HashMap::new()),
+ type_map: RefCell::new(TypeMap::new()),
namespace_map: RefCell::new(HashMap::new()),
composite_types_completed: RefCell::new(HashSet::new()),
};
@@ -1255,7 +1638,8 @@ impl MemberDescriptionFactory {
// more information.
enum RecursiveTypeDescription {
UnfinishedMetadata {
- cache_id: uint,
+ unfinished_type: ty::t,
+ unique_type_id: UniqueTypeId,
metadata_stub: DICompositeType,
llvm_type: Type,
file_metadata: DIFile,
@@ -1264,6 +1648,31 @@ enum RecursiveTypeDescription {
FinalMetadata(DICompositeType)
}
+fn create_and_register_recursive_type_forward_declaration(
+ cx: &CrateContext,
+ unfinished_type: ty::t,
+ unique_type_id: UniqueTypeId,
+ metadata_stub: DICompositeType,
+ llvm_type: Type,
+ file_metadata: DIFile,
+ member_description_factory: MemberDescriptionFactory)
+ -> RecursiveTypeDescription {
+
+ // Insert the stub into the TypeMap in order to allow for recursive references
+ let mut type_map = debug_context(cx).type_map.borrow_mut();
+ type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata_stub);
+ type_map.register_type_with_metadata(cx, unfinished_type, metadata_stub);
+
+ UnfinishedMetadata {
+ unfinished_type: unfinished_type,
+ unique_type_id: unique_type_id,
+ metadata_stub: metadata_stub,
+ llvm_type: llvm_type,
+ file_metadata: file_metadata,
+ member_description_factory: member_description_factory,
+ }
+}
+
impl RecursiveTypeDescription {
// Finishes up the description of the type in question (mostly by providing descriptions of the
// fields of the given type) and returns the final type metadata.
@@ -1271,15 +1680,27 @@ impl RecursiveTypeDescription {
match *self {
FinalMetadata(metadata) => metadata,
UnfinishedMetadata {
- cache_id,
+ unfinished_type,
+ unique_type_id,
metadata_stub,
llvm_type,
file_metadata,
ref member_description_factory
} => {
- // Insert the stub into the cache in order to allow recursive references ...
- debug_context(cx).created_types.borrow_mut()
- .insert(cache_id, metadata_stub);
+ // Make sure that we have a forward declaration of the type in the TypeMap so that
+ // recursive references are possible. This will always be the case if the
+ // RecursiveTypeDescription has been properly created through the
+ // create_and_register_recursive_type_forward_declaration() function.
+ {
+ let type_map = debug_context(cx).type_map.borrow();
+ if type_map.find_metadata_for_unique_id(unique_type_id).is_none() ||
+ type_map.find_metadata_for_type(unfinished_type).is_none() {
+ cx.sess().bug(format!("Forward declaration of potentially recursive type \
+ '{}' was not found in TypeMap!",
+ ppaux::ty_to_str(cx.tcx(), unfinished_type))
+ .as_slice());
+ }
+ }
// ... then create the member descriptions ...
let member_descriptions = member_description_factory.create_member_descriptions(cx);
@@ -1349,6 +1770,7 @@ fn prepare_struct_metadata(cx: &CrateContext,
struct_type: ty::t,
def_id: ast::DefId,
substs: &subst::Substs,
+ unique_type_id: UniqueTypeId,
span: Span)
-> RecursiveTypeDescription {
let struct_name = ppaux::ty_to_str(cx.tcx(), struct_type);
@@ -1362,23 +1784,26 @@ fn prepare_struct_metadata(cx: &CrateContext,
let struct_metadata_stub = create_struct_stub(cx,
struct_llvm_type,
struct_name.as_slice(),
+ unique_type_id,
containing_scope,
file_metadata,
definition_span);
let fields = ty::struct_fields(cx.tcx(), def_id, substs);
- UnfinishedMetadata {
- cache_id: cache_id_for_type(struct_type),
- metadata_stub: struct_metadata_stub,
- llvm_type: struct_llvm_type,
- file_metadata: file_metadata,
- member_description_factory: StructMDF(StructMemberDescriptionFactory {
+ create_and_register_recursive_type_forward_declaration(
+ cx,
+ struct_type,
+ unique_type_id,
+ struct_metadata_stub,
+ struct_llvm_type,
+ file_metadata,
+ StructMDF(StructMemberDescriptionFactory {
fields: fields,
is_simd: ty::type_is_simd(cx.tcx(), struct_type),
span: span,
- }),
- }
+ })
+ )
}
@@ -1409,6 +1834,7 @@ impl TupleMemberDescriptionFactory {
fn prepare_tuple_metadata(cx: &CrateContext,
tuple_type: ty::t,
component_types: &[ty::t],
+ unique_type_id: UniqueTypeId,
span: Span)
-> RecursiveTypeDescription {
let tuple_name = ppaux::ty_to_str(cx.tcx(), tuple_type);
@@ -1417,21 +1843,24 @@ fn prepare_tuple_metadata(cx: &CrateContext,
let loc = span_start(cx, span);
let file_metadata = file_metadata(cx, loc.file.name.as_slice());
- UnfinishedMetadata {
- cache_id: cache_id_for_type(tuple_type),
- metadata_stub: create_struct_stub(cx,
- tuple_llvm_type,
- tuple_name.as_slice(),
- file_metadata,
- file_metadata,
- span),
- llvm_type: tuple_llvm_type,
- file_metadata: file_metadata,
- member_description_factory: TupleMDF(TupleMemberDescriptionFactory {
+ create_and_register_recursive_type_forward_declaration(
+ cx,
+ tuple_type,
+ unique_type_id,
+ create_struct_stub(cx,
+ tuple_llvm_type,
+ tuple_name.as_slice(),
+ unique_type_id,
+ file_metadata,
+ file_metadata,
+ span),
+ tuple_llvm_type,
+ file_metadata,
+ TupleMDF(TupleMemberDescriptionFactory {
component_types: Vec::from_slice(component_types),
span: span,
})
- }
+ )
}
@@ -1444,6 +1873,7 @@ fn prepare_tuple_metadata(cx: &CrateContext,
// variant of the given enum, this factory will produce one MemberDescription (all with no name and
// a fixed offset of zero bytes).
struct EnumMemberDescriptionFactory {
+ enum_type: ty::t,
type_rep: Rc<adt::Repr>,
variants: Rc<Vec<Rc<ty::VariantInfo>>>,
discriminant_type_metadata: Option<DIType>,
@@ -1465,6 +1895,7 @@ impl EnumMemberDescriptionFactory {
.map(|(i, struct_def)| {
let (variant_type_metadata, variant_llvm_type, member_desc_factory) =
describe_enum_variant(cx,
+ self.enum_type,
struct_def,
&**self.variants.get(i),
discriminant_info,
@@ -1497,6 +1928,7 @@ impl EnumMemberDescriptionFactory {
} else {
let (variant_type_metadata, variant_llvm_type, member_description_factory) =
describe_enum_variant(cx,
+ self.enum_type,
struct_def,
&**self.variants.get(0),
NoDiscriminant,
@@ -1551,11 +1983,19 @@ impl EnumMemberDescriptionFactory {
offset: FixedMemberOffset { bytes: 0 },
};
+ let unique_type_id = debug_context(cx).type_map
+ .borrow_mut()
+ .get_unique_type_id_of_enum_variant(
+ cx,
+ self.enum_type,
+ non_null_variant_name.get());
+
// Now we can create the metadata of the artificial struct
let artificial_struct_metadata =
composite_type_metadata(cx,
artificial_struct_llvm_type,
non_null_variant_name.get(),
+ unique_type_id,
&[sole_struct_member_description],
self.containing_scope,
self.file_metadata,
@@ -1581,6 +2021,7 @@ impl EnumMemberDescriptionFactory {
// Create a description of the non-null variant
let (variant_type_metadata, variant_llvm_type, member_description_factory) =
describe_enum_variant(cx,
+ self.enum_type,
struct_def,
&**self.variants.get(nndiscr as uint),
OptimizedDiscriminant(ptrfield),
@@ -1654,6 +2095,7 @@ enum EnumDiscriminantInfo {
// (3) a MemberDescriptionFactory for producing the descriptions of the fields of the variant. This
// is a rudimentary version of a full RecursiveTypeDescription.
fn describe_enum_variant(cx: &CrateContext,
+ enum_type: ty::t,
struct_def: &adt::Struct,
variant_info: &ty::VariantInfo,
discriminant_info: EnumDiscriminantInfo,
@@ -1678,9 +2120,19 @@ fn describe_enum_variant(cx: &CrateContext,
codemap::DUMMY_SP
};
+ let variant_name = token::get_ident(variant_info.name);
+ let variant_name = variant_name.get();
+ let unique_type_id = debug_context(cx).type_map
+ .borrow_mut()
+ .get_unique_type_id_of_enum_variant(
+ cx,
+ enum_type,
+ variant_name);
+
let metadata_stub = create_struct_stub(cx,
variant_llvm_type,
- token::get_ident(variant_info.name).get(),
+ variant_name,
+ unique_type_id,
containing_scope,
file_metadata,
variant_definition_span);
@@ -1724,6 +2176,7 @@ fn describe_enum_variant(cx: &CrateContext,
fn prepare_enum_metadata(cx: &CrateContext,
enum_type: ty::t,
enum_def_id: ast::DefId,
+ unique_type_id: UniqueTypeId,
span: Span)
-> RecursiveTypeDescription {
let enum_name = ppaux::ty_to_str(cx.tcx(), enum_type);
@@ -1804,10 +2257,14 @@ fn prepare_enum_metadata(cx: &CrateContext,
let enum_llvm_type = type_of::type_of(cx, enum_type);
let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type);
- let unique_id = generate_unique_type_id("DI_ENUM_");
+
+ let unique_type_id_str = debug_context(cx)
+ .type_map
+ .borrow()
+ .get_unique_type_id_as_string(unique_type_id);
let enum_metadata = enum_name.as_slice().with_c_str(|enum_name| {
- unique_id.as_slice().with_c_str(|unique_id| {
+ unique_type_id_str.as_slice().with_c_str(|unique_type_id_str| {
unsafe {
llvm::LLVMDIBuilderCreateUnionType(
DIB(cx),
@@ -1820,17 +2277,20 @@ fn prepare_enum_metadata(cx: &CrateContext,
0, // Flags
ptr::null(),
0, // RuntimeLang
- unique_id)
+ unique_type_id_str)
}
})
});
- return UnfinishedMetadata {
- cache_id: cache_id_for_type(enum_type),
- metadata_stub: enum_metadata,
- llvm_type: enum_llvm_type,
- file_metadata: file_metadata,
- member_description_factory: EnumMDF(EnumMemberDescriptionFactory {
+ return create_and_register_recursive_type_forward_declaration(
+ cx,
+ enum_type,
+ unique_type_id,
+ enum_metadata,
+ enum_llvm_type,
+ file_metadata,
+ EnumMDF(EnumMemberDescriptionFactory {
+ enum_type: enum_type,
type_rep: type_rep.clone(),
variants: variants,
discriminant_type_metadata: discriminant_type_metadata,
@@ -1838,7 +2298,7 @@ fn prepare_enum_metadata(cx: &CrateContext,
file_metadata: file_metadata,
span: span,
}),
- };
+ );
fn get_enum_discriminant_name(cx: &CrateContext, def_id: ast::DefId) -> token::InternedString {
let name = if def_id.krate == ast::LOCAL_CRATE {
@@ -1857,6 +2317,7 @@ fn prepare_enum_metadata(cx: &CrateContext,
fn composite_type_metadata(cx: &CrateContext,
composite_llvm_type: Type,
composite_type_name: &str,
+ composite_type_unique_id: UniqueTypeId,
member_descriptions: &[MemberDescription],
containing_scope: DIScope,
file_metadata: DIFile,
@@ -1866,10 +2327,10 @@ fn composite_type_metadata(cx: &CrateContext,
let composite_type_metadata = create_struct_stub(cx,
composite_llvm_type,
composite_type_name,
+ composite_type_unique_id,
containing_scope,
file_metadata,
definition_span);
-
// ... and immediately create and add the member descriptions.
set_members_of_composite_type(cx,
composite_type_metadata,
@@ -1944,6 +2405,7 @@ fn set_members_of_composite_type(cx: &CrateContext,
fn create_struct_stub(cx: &CrateContext,
struct_llvm_type: Type,
struct_type_name: &str,
+ unique_type_id: UniqueTypeId,
containing_scope: DIScope,
file_metadata: DIFile,
definition_span: Span)
@@ -1951,13 +2413,12 @@ fn create_struct_stub(cx: &CrateContext,
let loc = span_start(cx, definition_span);
let (struct_size, struct_align) = size_and_align_of(cx, struct_llvm_type);
- // We assign unique IDs to the type stubs so LLVM metadata uniquing does not reuse instances
- // where we don't want it.
- let unique_id = generate_unique_type_id("DI_STRUCT_");
-
- return unsafe {
+ let unique_type_id_str = debug_context(cx).type_map
+ .borrow()
+ .get_unique_type_id_as_string(unique_type_id);
+ let metadata_stub = unsafe {
struct_type_name.with_c_str(|name| {
- unique_id.as_slice().with_c_str(|unique_id| {
+ unique_type_id_str.as_slice().with_c_str(|unique_type_id| {
// LLVMDIBuilderCreateStructType() wants an empty array. A null pointer will lead to
// hard to trace and debug LLVM assertions later on in llvm/lib/IR/Value.cpp
let empty_array = create_DIArray(DIB(cx), []);
@@ -1975,23 +2436,23 @@ fn create_struct_stub(cx: &CrateContext,
empty_array,
0,
ptr::null(),
- unique_id)
+ unique_type_id)
})
})
};
+
+ return metadata_stub;
}
-fn boxed_type_metadata(cx: &CrateContext,
- content_type_name: Option<&str>,
- content_llvm_type: Type,
- content_type_metadata: DIType,
- span: Span)
- -> DICompositeType {
- let box_type_name = match content_type_name {
- Some(content_type_name) => format!("Boxed<{}>", content_type_name),
- None => "BoxedType".to_string()
- };
+fn at_box_metadata(cx: &CrateContext,
+ content_type: ty::t,
+ unique_type_id: UniqueTypeId) -> DIType {
+ let content_type_name = ppaux::ty_to_str(cx.tcx(), content_type);
+ let content_type_name = content_type_name.as_slice();
+ let content_llvm_type = type_of::type_of(cx, content_type);
+ let content_type_metadata = type_metadata(cx, content_type, codemap::DUMMY_SP);
+ let box_type_name = format!("Boxed<{}>", content_type_name);
let box_llvm_type = Type::at_box(cx, content_llvm_type);
let member_llvm_types = box_llvm_type.field_types();
assert!(box_layout_is_correct(cx,
@@ -2035,17 +2496,18 @@ fn boxed_type_metadata(cx: &CrateContext,
}
];
- let loc = span_start(cx, span);
+ let loc = span_start(cx, codemap::DUMMY_SP);
let file_metadata = file_metadata(cx, loc.file.name.as_slice());
return composite_type_metadata(
cx,
box_llvm_type,
box_type_name.as_slice(),
+ unique_type_id,
member_descriptions,
file_metadata,
file_metadata,
- span);
+ codemap::DUMMY_SP);
// Unfortunately, we cannot assert anything but the correct types here---and not whether the
// 'next' and 'prev' pointers are in the correct order.
@@ -2062,6 +2524,7 @@ fn boxed_type_metadata(cx: &CrateContext,
}
}
+
fn fixed_vec_metadata(cx: &CrateContext,
element_type: ty::t,
len: uint,
@@ -2089,18 +2552,19 @@ fn fixed_vec_metadata(cx: &CrateContext,
};
}
-fn vec_metadata(cx: &CrateContext,
- element_type: ty::t,
- span: Span)
- -> DICompositeType {
+fn heap_vec_metadata(cx: &CrateContext,
+ vec_type: ty::t,
+ element_type: ty::t,
+ unique_type_id: UniqueTypeId,
+ span: Span)
+ -> DICompositeType {
let element_type_metadata = type_metadata(cx, element_type, span);
let element_llvm_type = type_of::type_of(cx, element_type);
let (element_size, element_align) = size_and_align_of(cx, element_llvm_type);
let vec_llvm_type = Type::vec(cx, &element_llvm_type);
- let vec_type_name = format!("[{}]",
- ppaux::ty_to_str(cx.tcx(), element_type));
+ let vec_type_name = ppaux::ty_to_str(cx.tcx(), vec_type);
let vec_type_name = vec_type_name.as_slice();
let member_llvm_types = vec_llvm_type.field_types();
@@ -2145,6 +2609,7 @@ fn vec_metadata(cx: &CrateContext,
cx,
vec_llvm_type,
vec_type_name,
+ unique_type_id,
member_descriptions,
file_metadata,
file_metadata,
@@ -2154,6 +2619,7 @@ fn vec_metadata(cx: &CrateContext,
fn vec_slice_metadata(cx: &CrateContext,
vec_type: ty::t,
element_type: ty::t,
+ unique_type_id: UniqueTypeId,
span: Span)
-> DICompositeType {
@@ -2196,6 +2662,7 @@ fn vec_slice_metadata(cx: &CrateContext,
cx,
slice_llvm_type,
slice_type_name.as_slice(),
+ unique_type_id,
member_descriptions,
file_metadata,
file_metadata,
@@ -2218,8 +2685,7 @@ fn subroutine_type_metadata(cx: &CrateContext,
let loc = span_start(cx, span);
let file_metadata = file_metadata(cx, loc.file.name.as_slice());
- let mut signature_metadata: Vec<DIType> =
- Vec::with_capacity(signature.inputs.len() + 1);
+ let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.inputs.len() + 1);
// return type
signature_metadata.push(match ty::get(signature.output).sty {
@@ -2245,7 +2711,8 @@ fn trait_metadata(cx: &CrateContext,
trait_type: ty::t,
substs: &subst::Substs,
trait_store: ty::TraitStore,
- _: &ty::BuiltinBounds)
+ _: &ty::BuiltinBounds,
+ unique_type_id: UniqueTypeId)
-> DIType {
// The implementation provided here is a stub. It makes sure that the trait type is
// assigned the correct name, size, namespace, and source location. But it does not describe
@@ -2272,49 +2739,55 @@ fn trait_metadata(cx: &CrateContext,
composite_type_metadata(cx,
trait_llvm_type,
name.as_slice(),
+ unique_type_id,
[],
containing_scope,
file_metadata,
definition_span)
}
+
fn type_metadata(cx: &CrateContext,
t: ty::t,
usage_site_span: Span)
-> DIType {
- let cache_id = cache_id_for_type(t);
-
- match debug_context(cx).created_types.borrow().find(&cache_id) {
- Some(type_metadata) => return *type_metadata,
- None => ()
- }
-
- fn create_pointer_to_box_metadata(cx: &CrateContext,
- pointer_type: ty::t,
- type_in_box: ty::t)
- -> DIType {
- let content_type_name = ppaux::ty_to_str(cx.tcx(), type_in_box);
- let content_type_name = content_type_name.as_slice();
- let content_llvm_type = type_of::type_of(cx, type_in_box);
- let content_type_metadata = type_metadata(
- cx,
- type_in_box,
- codemap::DUMMY_SP);
-
- let box_metadata = boxed_type_metadata(
- cx,
- Some(content_type_name),
- content_llvm_type,
- content_type_metadata,
- codemap::DUMMY_SP);
-
- pointer_type_metadata(cx, pointer_type, box_metadata)
- }
+ // Get the unique type id of this type.
+ let unique_type_id = {
+ let mut type_map = debug_context(cx).type_map.borrow_mut();
+ match type_map.find_metadata_for_type(t) {
+ Some(metadata) => {
+ return metadata;
+ },
+ None => {
+ let unique_type_id = type_map.get_unique_type_id_of_type(cx, t);
+ match type_map.find_metadata_for_unique_id(unique_type_id) {
+ Some(metadata) => {
+ type_map.register_type_with_metadata(cx, t, metadata);
+ return metadata;
+ },
+ None => {
+ // There really is no type metadata for this type, so proceed by creating
+ // it
+ unique_type_id
+ }
+ }
+ }
+ }
+ };
debug!("type_metadata: {:?}", ty::get(t));
+ macro_rules! return_if_created_in_meantime(
+ () => (
+ match debug_context(cx).type_map.borrow().find_metadata_for_unique_id(unique_type_id) {
+ Some(metadata) => return metadata,
+ None => { /* proceed normally */ }
+ };
+ )
+ )
+
let sty = &ty::get(t).sty;
- let type_metadata = match *sty {
+ let (type_metadata, should_already_be_stored_in_typemap) = match *sty {
ty::ty_nil |
ty::ty_bot |
ty::ty_bool |
@@ -2322,50 +2795,73 @@ fn type_metadata(cx: &CrateContext,
ty::ty_int(_) |
ty::ty_uint(_) |
ty::ty_float(_) => {
- basic_type_metadata(cx, t)
+ (basic_type_metadata(cx, t), false)
}
ty::ty_enum(def_id, _) => {
- prepare_enum_metadata(cx, t, def_id, usage_site_span).finalize(cx)
+ let is_c_style_enum = match *adt::represent_type(cx, t) {
+ adt::CEnum(..) => true,
+ _ => false
+ };
+
+ (prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx),
+ !is_c_style_enum)
}
- ty::ty_box(typ) => {
- create_pointer_to_box_metadata(cx, t, typ)
+ ty::ty_box(pointee_type) => {
+ let box_content_metadata = at_box_metadata(cx, pointee_type, unique_type_id);
+ return_if_created_in_meantime!();
+ (pointer_type_metadata(cx, t, box_content_metadata), false)
}
- ty::ty_vec(ref mt, Some(len)) => fixed_vec_metadata(cx, mt.ty, len, usage_site_span),
- ty::ty_uniq(typ) => {
- match ty::get(typ).sty {
+ ty::ty_vec(ref mt, Some(len)) => {
+ (fixed_vec_metadata(cx, mt.ty, len, usage_site_span), false)
+ }
+ ty::ty_uniq(pointee_type) => {
+ (match ty::get(pointee_type).sty {
ty::ty_vec(ref mt, None) => {
- let vec_metadata = vec_metadata(cx, mt.ty, usage_site_span);
+ let vec_metadata = heap_vec_metadata(cx,
+ pointee_type,
+ mt.ty,
+ unique_type_id,
+ usage_site_span);
+ return_if_created_in_meantime!();
pointer_type_metadata(cx, t, vec_metadata)
}
ty::ty_str => {
let i8_t = ty::mk_i8();
- let vec_metadata = vec_metadata(cx, i8_t, usage_site_span);
+ let vec_metadata = heap_vec_metadata(cx,
+ pointee_type,
+ i8_t,
+ unique_type_id,
+ usage_site_span);
pointer_type_metadata(cx, t, vec_metadata)
}
_ => {
- let pointee = type_metadata(cx, typ, usage_site_span);
- pointer_type_metadata(cx, t, pointee)
+ let pointee_metadata = type_metadata(cx, pointee_type, usage_site_span);
+ return_if_created_in_meantime!();
+ pointer_type_metadata(cx, t, pointee_metadata)
}
- }
+ }, false)
}
ty::ty_ptr(ref mt) | ty::ty_rptr(_, ref mt) => {
- match ty::get(mt.ty).sty {
- ty::ty_vec(ref mt, None) => vec_slice_metadata(cx, t, mt.ty, usage_site_span),
+ (match ty::get(mt.ty).sty {
+ ty::ty_vec(ref mt, None) => {
+ vec_slice_metadata(cx, t, mt.ty, unique_type_id, usage_site_span)
+ }
ty::ty_str => {
let i8_t = ty::mk_i8();
- vec_slice_metadata(cx, t, i8_t, usage_site_span)
+ vec_slice_metadata(cx, t, i8_t, unique_type_id, usage_site_span)
}
_ => {
let pointee = type_metadata(cx, mt.ty, usage_site_span);
+ return_if_created_in_meantime!();
pointer_type_metadata(cx, t, pointee)
}
- }
+ }, false)
}
ty::ty_bare_fn(ref barefnty) => {
- subroutine_type_metadata(cx, &barefnty.sig, usage_site_span)
+ (subroutine_type_metadata(cx, &barefnty.sig, usage_site_span), false)
}
ty::ty_closure(ref closurety) => {
- subroutine_type_metadata(cx, &closurety.sig, usage_site_span)
+ (subroutine_type_metadata(cx, &closurety.sig, usage_site_span), false)
}
ty::ty_trait(box ty::TyTrait {
def_id,
@@ -2373,25 +2869,60 @@ fn type_metadata(cx: &CrateContext,
store,
ref bounds
}) => {
- trait_metadata(cx, def_id, t, substs, store, bounds)
+ (trait_metadata(cx, def_id, t, substs, store, bounds, unique_type_id), false)
}
ty::ty_struct(def_id, ref substs) => {
- prepare_struct_metadata(cx, t, def_id, substs, usage_site_span).finalize(cx)
+ let struct_metadata = prepare_struct_metadata(cx,
+ t,
+ def_id,
+ substs,
+ unique_type_id,
+ usage_site_span).finalize(cx);
+ (struct_metadata, true)
}
ty::ty_tup(ref elements) => {
- prepare_tuple_metadata(cx,
- t,
- elements.as_slice(),
- usage_site_span).finalize(cx)
+ let tuple_metadata = prepare_tuple_metadata(cx,
+ t,
+ elements.as_slice(),
+ unique_type_id,
+ usage_site_span).finalize(cx);
+ (tuple_metadata, true)
}
_ => {
- cx.sess().bug(format!("debuginfo: unexpected type in \
- type_metadata: {:?}",
+ cx.sess().bug(format!("debuginfo: unexpected type in type_metadata: {:?}",
sty).as_slice())
}
};
- debug_context(cx).created_types.borrow_mut().insert(cache_id, type_metadata);
+ {
+ let mut type_map = debug_context(cx).type_map.borrow_mut();
+
+ if should_already_be_stored_in_typemap {
+ // Make sure that we already have a TypeMap entry entry for the ty::t.
+ if type_map.find_metadata_for_type(t).is_none() {
+ let unique_type_id_str = type_map.get_unique_type_id_as_string(unique_type_id);
+ let error_message = format!("Expected type metadata for ty::t '{}' to already be in
+ the TypeMap but it was not (unique type id = {})",
+ ppaux::ty_to_str(cx.tcx(), t),
+ unique_type_id_str.as_slice());
+ cx.sess().span_bug(usage_site_span, error_message.as_slice());
+ }
+
+ // Also make sure that we already have a TypeMap entry entry for the unique type id.
+ if type_map.find_metadata_for_unique_id(unique_type_id).is_none() {
+ let unique_type_id_str = type_map.get_unique_type_id_as_string(unique_type_id);
+ let error_message = format!("Expected type metadata for unique type id '{}' to
+ already be in the TypeMap but it was not. (ty::t = {})",
+ unique_type_id_str.as_slice(),
+ ppaux::ty_to_str(cx.tcx(), t));
+ cx.sess().span_bug(usage_site_span, error_message.as_slice());
+ }
+ } else {
+ type_map.register_type_with_metadata(cx, t, type_metadata);
+ type_map.register_unique_id_with_metadata(cx, unique_type_id, type_metadata);
+ }
+ }
+
type_metadata
}
@@ -2446,20 +2977,6 @@ fn set_debug_location(cx: &CrateContext, debug_location: DebugLocation) {
// Utility Functions
//=-------------------------------------------------------------------------------------------------
-fn cache_id_for_type(t: ty::t) -> uint {
- ty::type_id(t)
-}
-
-// Used to avoid LLVM metadata uniquing problems. See `create_struct_stub()` and
-// `prepare_enum_metadata()`.
-fn generate_unique_type_id(prefix: &'static str) -> String {
- unsafe {
- static mut unique_id_counter: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
- format!("{}{}", prefix,
- unique_id_counter.fetch_add(1, atomics::SeqCst))
- }
-}
-
/// Return codemap::Loc corresponding to the beginning of the span
fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc {
cx.sess().codemap().lookup_char_pos(span.lo)
View
26 src/test/auxiliary/cross_crate_debuginfo_type_uniquing.rs
@@ -0,0 +1,26 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+#![crate_type = "rlib"]
+// compile-flags:-g
+
+struct S1;
+
+impl S1 {
+ fn f(&mut self) { }
+}
+
+
+struct S2;
+
+impl S2 {
+ fn f(&mut self) { }
+}
View
24 src/test/debuginfo/cross-crate-type-uniquing.rs
@@ -0,0 +1,24 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-android: FIXME(#10381)
+
+// aux-build:cross_crate_debuginfo_type_uniquing.rs
+extern crate cross_crate_debuginfo_type_uniquing;
+
+// no-prefer-dynamic
+// compile-flags:-g -Zlto
+
+pub struct C;
+pub fn p() -> C {
+ C
+}
+
+fn main() { }
Please sign in to comment.
Something went wrong with that request. Please try again.