Skip to content

Commit 8b37e80

Browse files
committed
Fix stack overflow when generating debuginfo for 'recursive' type
By using 'impl trait', it's possible to create a self-referential type as follows: fn foo() -> impl Copy { foo } This is a function which returns itself. Normally, the signature of this function would be impossible to write - it would look like 'fn foo() -> fn() -> fn() ...' e.g. a function which returns a function, which returns a function... Using 'impl trait' allows us to avoid writing this infinitely long type. While it's useless for practical purposes, it does compile and run However, issues arise when we try to generate llvm debuginfo for such a type. All 'impl trait' types (e.g. ty::Opaque) are resolved when we generate debuginfo, which can lead to us recursing back to the original 'fn' type when we try to process its return type. To resolve this, I've modified debuginfo generation to account for these kinds of weird types. Unfortunately, there's no 'correct' debuginfo that we can generate - 'impl trait' does not exist in debuginfo, and this kind of recursive type is impossible to directly represent. To ensure that we emit *something*, this commit emits dummy debuginfo/type names whenever it encounters a self-reference. In practice, this should never happen - it's just to ensure that we can emit some kind of debuginfo, even if it's not particularly meaningful Fixes rust-lang#58463
1 parent 4691471 commit 8b37e80

File tree

3 files changed

+108
-17
lines changed

3 files changed

+108
-17
lines changed

src/librustc_codegen_llvm/debuginfo/metadata.rs

+64-4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,32 @@ impl TypeMap<'ll, 'tcx> {
116116
}
117117
}
118118

119+
// Removes a Ty to metadata mapping
120+
// This is useful when computing the metadata for a potentially
121+
// recursive type (e.g. a function ptr of the form:
122+
//
123+
// fn foo() -> impl Copy { foo }
124+
//
125+
// This kind of type cannot be properly represented
126+
// via LLVM debuginfo. As a workaround,
127+
// we register a temporary Ty to metadata mapping
128+
// for the function before we compute its actual metadat.a
129+
// If the metadata computation ends up recursing back to the
130+
// original function, it will use the temporary mapping
131+
// for the inner self-reference, preventing us from
132+
// recursing forever.
133+
//
134+
// This function is used to remove the temporary metadata
135+
// mapping after we've computed the actual metadat
136+
fn remove_type(
137+
&mut self,
138+
type_: Ty<'tcx>,
139+
) {
140+
if self.type_to_metadata.remove(type_).is_none() {
141+
bug!("Type metadata Ty '{}' is not in the TypeMap!", type_);
142+
}
143+
}
144+
119145
// Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will
120146
// fail if the mapping already exists.
121147
fn register_unique_id_with_metadata(
@@ -596,17 +622,51 @@ pub fn type_metadata(
596622
}
597623
}
598624
ty::FnDef(..) | ty::FnPtr(_) => {
599-
let fn_metadata = subroutine_type_metadata(cx,
600-
unique_type_id,
601-
t.fn_sig(cx.tcx),
602-
usage_site_span).metadata;
625+
603626
if let Some(metadata) = debug_context(cx).type_map
604627
.borrow()
605628
.find_metadata_for_unique_id(unique_type_id)
606629
{
607630
return metadata;
608631
}
609632

633+
// It's possible to create a self-referential
634+
// type in Rust by using 'impl trait':
635+
//
636+
// fn foo() -> impl Copy { foo }
637+
//
638+
// See TypeMap::remove_type for more detals
639+
// about the workaround
640+
641+
let temp_type = {
642+
unsafe {
643+
// The choice of type here is pretty arbitrary -
644+
// anything reading the debuginfo for a recursive
645+
// type is going to see *somthing* weird - the only
646+
// question is what exactly it will see
647+
let (size, align) = cx.size_and_align_of(t);
648+
llvm::LLVMRustDIBuilderCreateBasicType(
649+
DIB(cx),
650+
SmallCStr::new("<recur_type>").as_ptr(),
651+
size.bits(),
652+
align.bits() as u32,
653+
DW_ATE_unsigned)
654+
655+
656+
}
657+
};
658+
659+
let type_map = &debug_context(cx).type_map;
660+
type_map.borrow_mut().register_type_with_metadata(t, temp_type);
661+
662+
let fn_metadata = subroutine_type_metadata(cx,
663+
unique_type_id,
664+
t.fn_sig(cx.tcx),
665+
usage_site_span).metadata;
666+
667+
type_map.borrow_mut().remove_type(t);
668+
669+
610670
// This is actually a function pointer, so wrap it in pointer DI
611671
MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false)
612672

src/librustc_codegen_llvm/debuginfo/type_names.rs

+36-13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc::hir::def_id::DefId;
55
use rustc::ty::subst::SubstsRef;
66
use rustc::ty::{self, Ty};
77
use rustc_codegen_ssa::traits::*;
8+
use rustc_data_structures::fx::FxHashSet;
89

910
use rustc::hir;
1011

@@ -17,7 +18,8 @@ pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
1718
qualified: bool)
1819
-> String {
1920
let mut result = String::with_capacity(64);
20-
push_debuginfo_type_name(cx, t, qualified, &mut result);
21+
let mut visited = FxHashSet::default();
22+
push_debuginfo_type_name(cx, t, qualified, &mut result, &mut visited);
2123
result
2224
}
2325

@@ -26,7 +28,27 @@ pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
2628
pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
2729
t: Ty<'tcx>,
2830
qualified: bool,
29-
output: &mut String) {
31+
output: &mut String,
32+
visited: &mut FxHashSet<Ty<'tcx>>) {
33+
34+
// We've encountered a weird 'recursive type'
35+
// Currently, the only way to generate such a type
36+
// is by using 'impl trait':
37+
//
38+
// fn foo() -> impl Copy { foo }
39+
//
40+
// There's not really a sensible name we can generate,
41+
// since we don't include 'impl trait' types (e.g. ty::Opaque)
42+
// in the output
43+
//
44+
// Since we need to generate *something*, we just
45+
// use a dummy string that should make it clear
46+
// that something unusual is going on
47+
if visited.insert(t) {
48+
output.push_str("<recursive_type>");
49+
return;
50+
}
51+
3052
// When targeting MSVC, emit C++ style type names for compatibility with
3153
// .natvis visualizers (and perhaps other existing native debuggers?)
3254
let cpp_like_names = cx.sess().target.target.options.is_like_msvc;
@@ -42,12 +64,12 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
4264
ty::Foreign(def_id) => push_item_name(cx, def_id, qualified, output),
4365
ty::Adt(def, substs) => {
4466
push_item_name(cx, def.did, qualified, output);
45-
push_type_params(cx, substs, output);
67+
push_type_params(cx, substs, output, visited);
4668
},
4769
ty::Tuple(component_types) => {
4870
output.push('(');
4971
for &component_type in component_types {
50-
push_debuginfo_type_name(cx, component_type, true, output);
72+
push_debuginfo_type_name(cx, component_type, true, output, visited);
5173
output.push_str(", ");
5274
}
5375
if !component_types.is_empty() {
@@ -65,7 +87,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
6587
hir::MutMutable => output.push_str("mut "),
6688
}
6789

68-
push_debuginfo_type_name(cx, inner_type, true, output);
90+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
6991

7092
if cpp_like_names {
7193
output.push('*');
@@ -79,15 +101,15 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
79101
output.push_str("mut ");
80102
}
81103

82-
push_debuginfo_type_name(cx, inner_type, true, output);
104+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
83105

84106
if cpp_like_names {
85107
output.push('*');
86108
}
87109
},
88110
ty::Array(inner_type, len) => {
89111
output.push('[');
90-
push_debuginfo_type_name(cx, inner_type, true, output);
112+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
91113
output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx)));
92114
output.push(']');
93115
},
@@ -98,7 +120,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
98120
output.push('[');
99121
}
100122

101-
push_debuginfo_type_name(cx, inner_type, true, output);
123+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
102124

103125
if cpp_like_names {
104126
output.push('>');
@@ -113,7 +135,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
113135
&principal,
114136
);
115137
push_item_name(cx, principal.def_id, false, output);
116-
push_type_params(cx, principal.substs, output);
138+
push_type_params(cx, principal.substs, output, visited);
117139
} else {
118140
output.push_str("dyn '_");
119141
}
@@ -136,7 +158,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
136158
let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
137159
if !sig.inputs().is_empty() {
138160
for &parameter_type in sig.inputs() {
139-
push_debuginfo_type_name(cx, parameter_type, true, output);
161+
push_debuginfo_type_name(cx, parameter_type, true, output, visited);
140162
output.push_str(", ");
141163
}
142164
output.pop();
@@ -155,7 +177,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
155177

156178
if !sig.output().is_unit() {
157179
output.push_str(" -> ");
158-
push_debuginfo_type_name(cx, sig.output(), true, output);
180+
push_debuginfo_type_name(cx, sig.output(), true, output, visited);
159181
}
160182
},
161183
ty::Closure(..) => {
@@ -200,15 +222,16 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
200222
// common denominator - otherwise we would run into conflicts.
201223
fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
202224
substs: SubstsRef<'tcx>,
203-
output: &mut String) {
225+
output: &mut String,
226+
visited: &mut FxHashSet<Ty<'tcx>>) {
204227
if substs.types().next().is_none() {
205228
return;
206229
}
207230

208231
output.push('<');
209232

210233
for type_parameter in substs.types() {
211-
push_debuginfo_type_name(cx, type_parameter, true, output);
234+
push_debuginfo_type_name(cx, type_parameter, true, output, visited);
212235
output.push_str(", ");
213236
}
214237

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-pass
2+
// compile-flags:-C debuginfo=2
3+
fn foo() -> impl Copy {
4+
foo
5+
}
6+
fn main() {
7+
foo();
8+
}

0 commit comments

Comments
 (0)