From 8e235258f38e43c4b565c5393d96aa5381d366bf Mon Sep 17 00:00:00 2001 From: Adwin White Date: Mon, 17 Mar 2025 18:17:09 +0800 Subject: [PATCH] fix(debuginfo): avoid overflow when handling expanding recursive type --- .../src/debuginfo/metadata/type_map.rs | 48 +++++++++++++++++++ .../rustc_codegen_llvm/src/debuginfo/mod.rs | 2 + tests/debuginfo/recursive-enum.rs | 17 ++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index af1d503ad6a4..8fd13eee379d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -247,6 +247,16 @@ pub(super) fn stub<'ll, 'tcx>( StubInfo { metadata, unique_type_id } } +struct AdtStackPopGuard<'ll, 'tcx, 'a> { + cx: &'a CodegenCx<'ll, 'tcx>, +} + +impl<'ll, 'tcx, 'a> Drop for AdtStackPopGuard<'ll, 'tcx, 'a> { + fn drop(&mut self) { + debug_context(self.cx).adt_stack.borrow_mut().pop(); + } +} + /// This function enables creating debuginfo nodes that can recursively refer to themselves. /// It will first insert the given stub into the type map and only then execute the `members` /// and `generics` closures passed in. These closures have access to the stub so they can @@ -261,6 +271,44 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( ) -> DINodeCreationResult<'ll> { assert_eq!(debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id), None); + let mut _adt_stack_pop_guard = None; + if let UniqueTypeId::Ty(ty, ..) = stub_info.unique_type_id + && let ty::Adt(adt_def, args) = ty.kind() + { + let def_id = adt_def.did(); + // If any sub type reference the original type definition and the sub type has a type + // parameter that strictly contains the original parameter, the original type is a recursive + // type that can expanding indefinitely. Example, + // ``` + // enum Recursive { + // Recurse(*const Recursive>), + // Item(T), + // } + // ``` + let is_expanding_recursive = + debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { + if def_id == *parent_def_id { + args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { + if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) + { + arg != parent_arg && arg.contains(parent_arg) + } else { + false + } + }) + } else { + false + } + }); + if is_expanding_recursive { + // FIXME: indicate that this is an expanding recursive type in stub metadata? + return DINodeCreationResult::new(stub_info.metadata, false); + } else { + debug_context(cx).adt_stack.borrow_mut().push((def_id, args)); + _adt_stack_pop_guard = Some(AdtStackPopGuard { cx }); + } + } + debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata); let members: SmallVec<_> = diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 10819a53b1df..bbe69382d084 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -65,6 +65,7 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { created_files: RefCell, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, + adt_stack: RefCell)>>, namespace_map: RefCell>, recursion_marker_type: OnceCell<&'ll DIType>, } @@ -79,6 +80,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { builder, created_files: Default::default(), type_map: Default::default(), + adt_stack: Default::default(), namespace_map: RefCell::new(Default::default()), recursion_marker_type: OnceCell::new(), } diff --git a/tests/debuginfo/recursive-enum.rs b/tests/debuginfo/recursive-enum.rs index c2c3e71b8a4f..b861e6d617c8 100644 --- a/tests/debuginfo/recursive-enum.rs +++ b/tests/debuginfo/recursive-enum.rs @@ -4,7 +4,7 @@ // gdb-command:run // Test whether compiling a recursive enum definition crashes debug info generation. The test case -// is taken from issue #11083. +// is taken from issue #11083 and #135093. #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] @@ -18,6 +18,21 @@ struct WindowCallbacks<'a> { pos_callback: Option>, } +enum ExpandingRecursive { + Recurse(Indirect), + Item(T), +} + +struct Indirect { + rec: *const ExpandingRecursive>, +} + + fn main() { let x = WindowCallbacks { pos_callback: None }; + + // EXPANDING RECURSIVE + let expanding_recursive: ExpandingRecursive = ExpandingRecursive::Recurse(Indirect { + rec: &ExpandingRecursive::Item(Option::Some(42)), + }); }