diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 280f0207116..d049bafb821 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -442,11 +442,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (var_ty, var_kind) = match var.value { mir::VarDebugInfoContents::Place(place) => { let var_ty = self.monomorphized_place_ty(place.as_ref()); - let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg + let var_kind = if let Some(arg_index) = var.argument_index && place.projection.is_empty() - && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE { - let arg_index = place.local.index() - 1; + let arg_index = arg_index as usize; if target_is_msvc { // ScalarPair parameters are spilled to the stack so they need to // be marked as a `LocalVariable` for MSVC debuggers to visualize @@ -455,13 +454,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Abi::ScalarPair(_, _) = var_ty_layout.abi { VariableKind::LocalVariable } else { - VariableKind::ArgumentVariable(arg_index + 1) + VariableKind::ArgumentVariable(arg_index) } } else { // FIXME(eddyb) shouldn't `ArgumentVariable` indices be // offset in closures to account for the hidden environment? - // Also, is this `+ 1` needed at all? - VariableKind::ArgumentVariable(arg_index + 1) + VariableKind::ArgumentVariable(arg_index) } } else { VariableKind::LocalVariable diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 56755e588cb..2ea8602af12 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1115,6 +1115,11 @@ pub struct VarDebugInfo<'tcx> { /// Where the data for this user variable is to be found. pub value: VarDebugInfoContents<'tcx>, + + /// When present, indicates what argument number this variable is in the function that it + /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the + /// argument number in the original function before it was inlined. + pub argument_index: Option, } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 6c4ea065abe..0a9fcd898b9 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -832,6 +832,7 @@ macro_rules! make_mir_visitor { name: _, source_info, value, + argument_index: _, } = var_debug_info; self.visit_source_info(source_info); diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 2d52102db2c..4926ff85de3 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -2242,6 +2242,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { name, source_info: debug_source_info, value: VarDebugInfoContents::Place(for_arm_body.into()), + argument_index: None, }); let locals = if has_guard.0 { let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { @@ -2260,6 +2261,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { name, source_info: debug_source_info, value: VarDebugInfoContents::Place(ref_for_guard.into()), + argument_index: None, }); LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 415f5b1b1e1..bc50bcbc3d0 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -811,6 +811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { name, source_info: SourceInfo::outermost(captured_place.var_ident.span), value: VarDebugInfoContents::Place(use_place), + argument_index: None, }); let capture = Capture { captured_place, use_place, mutability }; @@ -827,7 +828,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd<()> { // Allocate locals for the function arguments - for param in arguments.iter() { + for (argument_index, param) in arguments.iter().enumerate() { let source_info = SourceInfo::outermost(param.pat.as_ref().map_or(self.fn_span, |pat| pat.span)); let arg_local = @@ -839,6 +840,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { name, source_info, value: VarDebugInfoContents::Place(arg_local.into()), + argument_index: Some(argument_index as u16 + 1), }); } } diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 159780319ba..4c4423721fb 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1556,6 +1556,13 @@ impl<'tcx> MirPass<'tcx> for StateTransform { body.arg_count = 2; // self, resume arg body.spread_arg = None; + // The original arguments to the function are no longer arguments, mark them as such. + // Otherwise they'll conflict with our new arguments, which although they don't have + // argument_index set, will get emitted as unnamed arguments. + for var in &mut body.var_debug_info { + var.argument_index = None; + } + body.generator.as_mut().unwrap().yield_ty = None; body.generator.as_mut().unwrap().generator_layout = Some(layout); diff --git a/tests/codegen/inline-function-args-debug-info.rs b/tests/codegen/inline-function-args-debug-info.rs new file mode 100644 index 00000000000..e3d8caa49d4 --- /dev/null +++ b/tests/codegen/inline-function-args-debug-info.rs @@ -0,0 +1,20 @@ +// This test checks that debug information includes function argument indexes even if the function +// gets inlined by MIR inlining. Without function argument indexes, `info args` in gdb won't show +// arguments and their values for the current function. + +// compile-flags: -Zinline-mir=yes -Cdebuginfo=2 --edition=2021 + +#![crate_type = "lib"] + +pub fn outer_function(x: usize, y: usize) -> usize { + inner_function(x, y) + 1 +} + +#[inline] +fn inner_function(aaaa: usize, bbbb: usize) -> usize { + // CHECK: !DILocalVariable(name: "aaaa", arg: 1 + // CHECK-SAME: line: 14 + // CHECK: !DILocalVariable(name: "bbbb", arg: 2 + // CHECK-SAME: line: 14 + aaaa + bbbb +} diff --git a/tests/ui/async-await/task-context-arg.rs b/tests/ui/async-await/task-context-arg.rs new file mode 100644 index 00000000000..937723ca743 --- /dev/null +++ b/tests/ui/async-await/task-context-arg.rs @@ -0,0 +1,25 @@ +// Checks that we don't get conflicting arguments in our debug info with a particular async function +// structure. + +// edition:2021 +// compile-flags: -Cdebuginfo=2 +// build-pass + +#![crate_type = "lib"] + +use std::future::Future; + +// The compiler produces a closure as part of this function. That closure initially takes an +// argument _task_context. Later, when the MIR for that closure is transformed into a generator +// state machine, _task_context is demoted to not be an argument, but just part of an unnamed +// argument. If we emit debug info saying that both _task_context and the unnamed argument are both +// argument number 2, then LLVM will fail with "conflicting debug info for argument". See +// https://github.com/rust-lang/rust/pull/109466#issuecomment-1500879195 for details. +async fn recv_unit() { + std::future::ready(()).await; +} + +pub fn poll_recv() { + // This box is necessary in order to reproduce the problem. + let _: Box> = Box::new(recv_unit()); +}