Skip to content

Commit 57a4f17

Browse files
authored
Rollup merge of rust-lang#59446 - Aaron1011:fix/debuginfo-overflow, r=oli-obk
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
2 parents 6bd01ef + c13daeb commit 57a4f17

File tree

4 files changed

+134
-17
lines changed

4 files changed

+134
-17
lines changed

src/librustc_codegen_llvm/debuginfo/metadata.rs

+62-4
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,32 @@ impl TypeMap<'ll, 'tcx> {
117117
}
118118
}
119119

120+
// Removes a Ty to metadata mapping
121+
// This is useful when computing the metadata for a potentially
122+
// recursive type (e.g. a function ptr of the form:
123+
//
124+
// fn foo() -> impl Copy { foo }
125+
//
126+
// This kind of type cannot be properly represented
127+
// via LLVM debuginfo. As a workaround,
128+
// we register a temporary Ty to metadata mapping
129+
// for the function before we compute its actual metadata.
130+
// If the metadata computation ends up recursing back to the
131+
// original function, it will use the temporary mapping
132+
// for the inner self-reference, preventing us from
133+
// recursing forever.
134+
//
135+
// This function is used to remove the temporary metadata
136+
// mapping after we've computed the actual metadata
137+
fn remove_type(
138+
&mut self,
139+
type_: Ty<'tcx>,
140+
) {
141+
if self.type_to_metadata.remove(type_).is_none() {
142+
bug!("Type metadata Ty '{}' is not in the TypeMap!", type_);
143+
}
144+
}
145+
120146
// Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will
121147
// fail if the mapping already exists.
122148
fn register_unique_id_with_metadata(
@@ -608,17 +634,49 @@ pub fn type_metadata(
608634
}
609635
}
610636
ty::FnDef(..) | ty::FnPtr(_) => {
611-
let fn_metadata = subroutine_type_metadata(cx,
612-
unique_type_id,
613-
t.fn_sig(cx.tcx),
614-
usage_site_span).metadata;
637+
615638
if let Some(metadata) = debug_context(cx).type_map
616639
.borrow()
617640
.find_metadata_for_unique_id(unique_type_id)
618641
{
619642
return metadata;
620643
}
621644

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

src/librustc_codegen_llvm/debuginfo/type_names.rs

+49-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,9 @@ 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+
3034
// When targeting MSVC, emit C++ style type names for compatibility with
3135
// .natvis visualizers (and perhaps other existing native debuggers?)
3236
let cpp_like_names = cx.sess().target.target.options.is_like_msvc;
@@ -42,12 +46,12 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
4246
ty::Foreign(def_id) => push_item_name(cx, def_id, qualified, output),
4347
ty::Adt(def, substs) => {
4448
push_item_name(cx, def.did, qualified, output);
45-
push_type_params(cx, substs, output);
49+
push_type_params(cx, substs, output, visited);
4650
},
4751
ty::Tuple(component_types) => {
4852
output.push('(');
4953
for &component_type in component_types {
50-
push_debuginfo_type_name(cx, component_type, true, output);
54+
push_debuginfo_type_name(cx, component_type, true, output, visited);
5155
output.push_str(", ");
5256
}
5357
if !component_types.is_empty() {
@@ -65,7 +69,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
6569
hir::MutMutable => output.push_str("mut "),
6670
}
6771

68-
push_debuginfo_type_name(cx, inner_type, true, output);
72+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
6973

7074
if cpp_like_names {
7175
output.push('*');
@@ -79,15 +83,15 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
7983
output.push_str("mut ");
8084
}
8185

82-
push_debuginfo_type_name(cx, inner_type, true, output);
86+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
8387

8488
if cpp_like_names {
8589
output.push('*');
8690
}
8791
},
8892
ty::Array(inner_type, len) => {
8993
output.push('[');
90-
push_debuginfo_type_name(cx, inner_type, true, output);
94+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
9195
output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx)));
9296
output.push(']');
9397
},
@@ -98,7 +102,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
98102
output.push('[');
99103
}
100104

101-
push_debuginfo_type_name(cx, inner_type, true, output);
105+
push_debuginfo_type_name(cx, inner_type, true, output, visited);
102106

103107
if cpp_like_names {
104108
output.push('>');
@@ -113,12 +117,31 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
113117
&principal,
114118
);
115119
push_item_name(cx, principal.def_id, false, output);
116-
push_type_params(cx, principal.substs, output);
120+
push_type_params(cx, principal.substs, output, visited);
117121
} else {
118122
output.push_str("dyn '_");
119123
}
120124
},
121125
ty::FnDef(..) | ty::FnPtr(_) => {
126+
// We've encountered a weird 'recursive type'
127+
// Currently, the only way to generate such a type
128+
// is by using 'impl trait':
129+
//
130+
// fn foo() -> impl Copy { foo }
131+
//
132+
// There's not really a sensible name we can generate,
133+
// since we don't include 'impl trait' types (e.g. ty::Opaque)
134+
// in the output
135+
//
136+
// Since we need to generate *something*, we just
137+
// use a dummy string that should make it clear
138+
// that something unusual is going on
139+
if !visited.insert(t) {
140+
output.push_str("<recursive_type>");
141+
return;
142+
}
143+
144+
122145
let sig = t.fn_sig(cx.tcx);
123146
if sig.unsafety() == hir::Unsafety::Unsafe {
124147
output.push_str("unsafe ");
@@ -136,7 +159,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
136159
let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
137160
if !sig.inputs().is_empty() {
138161
for &parameter_type in sig.inputs() {
139-
push_debuginfo_type_name(cx, parameter_type, true, output);
162+
push_debuginfo_type_name(cx, parameter_type, true, output, visited);
140163
output.push_str(", ");
141164
}
142165
output.pop();
@@ -155,8 +178,20 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
155178

156179
if !sig.output().is_unit() {
157180
output.push_str(" -> ");
158-
push_debuginfo_type_name(cx, sig.output(), true, output);
181+
push_debuginfo_type_name(cx, sig.output(), true, output, visited);
159182
}
183+
184+
185+
// We only keep the type in 'visited'
186+
// for the duration of the body of this method.
187+
// It's fine for a particular function type
188+
// to show up multiple times in one overall type
189+
// (e.g. MyType<fn() -> u8, fn() -> u8>
190+
//
191+
// We only care about avoiding recursing
192+
// directly back to the type we're currently
193+
// processing
194+
visited.remove(t);
160195
},
161196
ty::Closure(..) => {
162197
output.push_str("closure");
@@ -200,15 +235,16 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
200235
// common denominator - otherwise we would run into conflicts.
201236
fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
202237
substs: SubstsRef<'tcx>,
203-
output: &mut String) {
238+
output: &mut String,
239+
visited: &mut FxHashSet<Ty<'tcx>>) {
204240
if substs.types().next().is_none() {
205241
return;
206242
}
207243

208244
output.push('<');
209245

210246
for type_parameter in substs.types() {
211-
push_debuginfo_type_name(cx, type_parameter, true, output);
247+
push_debuginfo_type_name(cx, type_parameter, true, output, visited);
212248
output.push_str(", ");
213249
}
214250

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// compile-flags: -g
2+
//
3+
// CHECK-LABEL: @main
4+
// CHECK: {{.*}}DIDerivedType(tag: DW_TAG_pointer_type, name: "fn() -> <recursive_type>",{{.*}}
5+
//
6+
// CHECK: {{.*}}DISubroutineType{{.*}}
7+
// CHECK: {{.*}}DIBasicType(name: "<recur_type>", encoding: DW_ATE_unsigned)
8+
9+
pub fn foo() -> impl Copy {
10+
foo
11+
}
12+
13+
fn main() {
14+
let my_res = foo();
15+
}
+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)