Skip to content

Conversation

@abidh
Copy link
Contributor

@abidh abidh commented Nov 11, 2025

CHARACTER dummy arguments were treated as local variables in debug info. This happened because our method to get the argument number was not robust. It relied on DeclareOp having a direct reference to arguments which was not the case for character arguments. This is fixed by storing source-level argument positions in DeclareOp.

Fixes #112886

@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir flang:openmp openacc flang:codegen labels Nov 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 11, 2025

@llvm/pr-subscribers-flang-codegen
@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-flang-fir-hlfir

Author: Abid Qadeer (abidh)

Changes

CHARACTER dummy arguments were treated as local variables in debug info. This happened because our method to get the argument number was not robust. It relied on DeclareOp having a direct reference to arguments which was not the case for character arguments. This is fixed by storing source-level argument positions in DeclareOp.

Fixes #112886


Patch is 920.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167489.diff

192 Files Affected:

  • (modified) flang/include/flang/Lower/AbstractConverter.h (+6)
  • (modified) flang/include/flang/Optimizer/Builder/HLFIRTools.h (+1-1)
  • (modified) flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td (+3-2)
  • (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+3-2)
  • (modified) flang/include/flang/Optimizer/HLFIR/HLFIROps.td (+4-3)
  • (modified) flang/lib/Lower/Bridge.cpp (+39-6)
  • (modified) flang/lib/Lower/ConvertVariable.cpp (+8-3)
  • (modified) flang/lib/Optimizer/Builder/FIRBuilder.cpp (+2-1)
  • (modified) flang/lib/Optimizer/Builder/HLFIRTools.cpp (+2-2)
  • (modified) flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp (+5-1)
  • (modified) flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp (+10-9)
  • (modified) flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp (+2-1)
  • (modified) flang/lib/Optimizer/Transforms/AddDebugInfo.cpp (+3-16)
  • (modified) flang/test/Fir/dispatch.f90 (+1-1)
  • (modified) flang/test/HLFIR/assumed-type-actual-args.f90 (+10-10)
  • (modified) flang/test/HLFIR/assumed_shape_with_value_keyword.f90 (+9-9)
  • (modified) flang/test/HLFIR/boxchar_emboxing.f90 (+2-2)
  • (modified) flang/test/HLFIR/c_ptr_byvalue.f90 (+1-1)
  • (modified) flang/test/HLFIR/call_with_poly_dummy.f90 (+1-1)
  • (modified) flang/test/HLFIR/optional_dummy.f90 (+1-1)
  • (added) flang/test/Integration/debug-char-arg-issue-112886.f90 (+46)
  • (modified) flang/test/Lower/CUDA/cuda-data-attribute.cuf (+4-4)
  • (modified) flang/test/Lower/CUDA/cuda-data-transfer.cuf (+8-8)
  • (modified) flang/test/Lower/HLFIR/actual_target_for_dummy_pointer.f90 (+11-11)
  • (modified) flang/test/Lower/HLFIR/allocatable-and-pointer-status-change.f90 (+6-6)
  • (modified) flang/test/Lower/HLFIR/allocatables-and-pointers.f90 (+9-9)
  • (modified) flang/test/Lower/HLFIR/array-ctor-as-elemental-nested.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/array-ctor-as-elemental.f90 (+5-5)
  • (modified) flang/test/Lower/HLFIR/array-ctor-as-inlined-temp.f90 (+6-6)
  • (modified) flang/test/Lower/HLFIR/array-ctor-index.f90 (+4-4)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-calls.f90 (+6-6)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-entry.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-iface-alloc-ptr.f90 (+5-5)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-iface.f90 (+9-9)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-inquiries-2.f90 (+5-5)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-inquiries.f90 (+24-24)
  • (modified) flang/test/Lower/HLFIR/assumed-rank-internal-proc.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/binary-ops.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/bindc-value-derived.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/call-sequence-associated-descriptors.f90 (+8-8)
  • (modified) flang/test/Lower/HLFIR/calls-array-results.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/calls-assumed-shape.f90 (+7-7)
  • (modified) flang/test/Lower/HLFIR/calls-constant-expr-arg.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/calls-f77.f90 (+7-7)
  • (modified) flang/test/Lower/HLFIR/calls-optional.f90 (+7-7)
  • (modified) flang/test/Lower/HLFIR/calls-percent-val-ref.f90 (+6-6)
  • (modified) flang/test/Lower/HLFIR/calls-poly-to-assumed-type.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/charconvert.f90 (+4-4)
  • (modified) flang/test/Lower/HLFIR/complex-div-to-hlfir-kind10.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/complex-div-to-hlfir.f90 (+12-12)
  • (modified) flang/test/Lower/HLFIR/convert-mbox-to-value.f90 (+8-8)
  • (modified) flang/test/Lower/HLFIR/convert-variable-assumed-rank.f90 (+9-9)
  • (modified) flang/test/Lower/HLFIR/convert-variable-block.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/convert-variable.f90 (+11-11)
  • (modified) flang/test/Lower/HLFIR/cray-pointers.f90 (+5-5)
  • (modified) flang/test/Lower/HLFIR/cshift.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/custom-intrinsic.f90 (+50-50)
  • (modified) flang/test/Lower/HLFIR/designators-component-ref.f90 (+4-4)
  • (modified) flang/test/Lower/HLFIR/designators.f90 (+20-20)
  • (modified) flang/test/Lower/HLFIR/dot_product.f90 (+2-2)
  • (added) flang/test/Lower/HLFIR/dummy-arg-number.f90 (+53)
  • (modified) flang/test/Lower/HLFIR/dummy-scope.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/elemental-array-ops.f90 (+6-6)
  • (modified) flang/test/Lower/HLFIR/elemental-polymorphic-merge.f90 (+4-4)
  • (modified) flang/test/Lower/HLFIR/elemental-result-length.f90 (+6-6)
  • (modified) flang/test/Lower/HLFIR/elemental-user-procedure-ref.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/eoshift.f90 (+11-11)
  • (modified) flang/test/Lower/HLFIR/expr-addr.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/expr-box.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/expr-value.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/ignore-rank-unlimited-polymorphic.f90 (+5-5)
  • (modified) flang/test/Lower/HLFIR/implicit-type-conversion.f90 (+14-14)
  • (modified) flang/test/Lower/HLFIR/index.f90 (+12-12)
  • (modified) flang/test/Lower/HLFIR/intentout-allocatable-components.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/internal-procedures.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/intrinsic-dynamically-optional.f90 (+2-2)
  • (modified) flang/test/Lower/HLFIR/procedure-pointer.f90 (+3-3)
  • (modified) flang/test/Lower/HLFIR/select-rank.f90 (+18-18)
  • (modified) flang/test/Lower/HLFIR/statement-functions.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/structure-constructor.f90 (+8-8)
  • (modified) flang/test/Lower/HLFIR/transformational.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/transpose.f90 (+5-5)
  • (modified) flang/test/Lower/HLFIR/trim.f90 (+1-1)
  • (modified) flang/test/Lower/HLFIR/user-defined-assignment.f90 (+19-19)
  • (modified) flang/test/Lower/HLFIR/vector-subscript-as-value.f90 (+3-3)
  • (modified) flang/test/Lower/Intrinsics/associated-proc-pointers.f90 (+6-6)
  • (modified) flang/test/Lower/Intrinsics/c_f_procpointer.f90 (+4-4)
  • (modified) flang/test/Lower/Intrinsics/c_funloc-proc-pointers.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90 (+4-4)
  • (modified) flang/test/Lower/Intrinsics/etime-function.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/etime.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/execute_command_line-optional.f90 (+5-5)
  • (modified) flang/test/Lower/Intrinsics/execute_command_line.f90 (+6-6)
  • (modified) flang/test/Lower/Intrinsics/getcwd-function.f90 (+1-1)
  • (modified) flang/test/Lower/Intrinsics/getcwd-optional.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/getcwd.f90 (+3-3)
  • (modified) flang/test/Lower/Intrinsics/ieee_logb.f90 (+1-1)
  • (modified) flang/test/Lower/Intrinsics/nearest.f90 (+14-14)
  • (modified) flang/test/Lower/Intrinsics/perror.f90 (+1-1)
  • (modified) flang/test/Lower/Intrinsics/putenv-sub.f90 (+3-3)
  • (modified) flang/test/Lower/Intrinsics/rename.f90 (+4-4)
  • (modified) flang/test/Lower/Intrinsics/second.f90 (+4-4)
  • (modified) flang/test/Lower/Intrinsics/selected_char_kind.f90 (+1-1)
  • (modified) flang/test/Lower/Intrinsics/selected_logical_kind.f90 (+5-5)
  • (modified) flang/test/Lower/Intrinsics/signal.f90 (+1-1)
  • (modified) flang/test/Lower/Intrinsics/sizeof.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/system-optional.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/system.f90 (+4-4)
  • (modified) flang/test/Lower/Intrinsics/unlink-sub.f90 (+3-3)
  • (modified) flang/test/Lower/OpenACC/acc-atomic-update-array.f90 (+9-9)
  • (modified) flang/test/Lower/OpenACC/acc-bounds.f90 (+5-5)
  • (modified) flang/test/Lower/OpenACC/acc-data-operands-remapping.f90 (+46-46)
  • (modified) flang/test/Lower/OpenACC/acc-declare.f90 (+7-7)
  • (modified) flang/test/Lower/OpenACC/acc-enter-data.f90 (+1-1)
  • (modified) flang/test/Lower/OpenACC/acc-firstprivate-derived-pointer-component.f90 (+2-2)
  • (modified) flang/test/Lower/OpenACC/acc-loop-exit.f90 (+1-1)
  • (modified) flang/test/Lower/OpenACC/acc-private.f90 (+6-6)
  • (modified) flang/test/Lower/OpenACC/acc-reduction.f90 (+4-4)
  • (modified) flang/test/Lower/OpenACC/acc-use-device.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/allocatable-array-bounds.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/array-bounds.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/cancel.f90 (+4-4)
  • (modified) flang/test/Lower/OpenMP/depend-complex.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/depend-substring.f90 (+4-4)
  • (modified) flang/test/Lower/OpenMP/flush.f90 (+6-6)
  • (modified) flang/test/Lower/OpenMP/optional-argument-map-2.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/parallel-firstprivate-clause-scalar.f90 (+21-21)
  • (modified) flang/test/Lower/OpenMP/parallel-lastprivate-clause-scalar.f90 (+9-9)
  • (modified) flang/test/Lower/OpenMP/parallel-private-clause-fixes.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/parallel-private-clause-str.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/parallel-reduction3.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/parallel-wsloop-firstpriv.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/parallel-wsloop-lastpriv.f90 (+5-5)
  • (modified) flang/test/Lower/OpenMP/parallel-wsloop.f90 (+11-11)
  • (modified) flang/test/Lower/OpenMP/reduction-array-intrinsic.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/sections-array-reduction.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/sections-reduction.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/sections.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/simd.f90 (+7-7)
  • (modified) flang/test/Lower/OpenMP/single.f90 (+6-6)
  • (modified) flang/test/Lower/OpenMP/target.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/task-depend-array-section.f90 (+4-4)
  • (modified) flang/test/Lower/OpenMP/tile01.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/tile02.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/unroll-heuristic01.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/unroll-heuristic02.f90 (+6-6)
  • (modified) flang/test/Lower/OpenMP/unroll-heuristic03.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-array-assumed-shape.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-iand-byref.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-iand.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-ieor-byref.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-ieor.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-ior-byref.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-ior.f90 (+1-1)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-and-byref.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-and.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-eqv-byref.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-eqv.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-neqv-byref.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-neqv.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-or-byref.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-logical-or.f90 (+3-3)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-max-byref.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-max.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-min-byref.f90 (+2-2)
  • (modified) flang/test/Lower/OpenMP/wsloop-reduction-min.f90 (+2-2)
  • (modified) flang/test/Lower/allocatable-assignment.f90 (+30-30)
  • (modified) flang/test/Lower/allocatable-polymorphic.f90 (+3-3)
  • (modified) flang/test/Lower/array-character.f90 (+2-2)
  • (modified) flang/test/Lower/array-elemental-calls-char-byval.f90 (+12-12)
  • (modified) flang/test/Lower/array-elemental-calls-char-dynamic.f90 (+11-11)
  • (modified) flang/test/Lower/array-elemental-calls-char.f90 (+15-15)
  • (modified) flang/test/Lower/box-address.f90 (+1-1)
  • (modified) flang/test/Lower/call-by-value-attr.f90 (+1-1)
  • (modified) flang/test/Lower/call-character-array-to-polymorphic-pointer.f90 (+1-1)
  • (modified) flang/test/Lower/character-substrings.f90 (+9-9)
  • (modified) flang/test/Lower/charconvert.f90 (+4-4)
  • (modified) flang/test/Lower/components.f90 (+7-7)
  • (modified) flang/test/Lower/derived-assignments.f90 (+12-12)
  • (modified) flang/test/Lower/dispatch.f90 (+15-15)
  • (modified) flang/test/Lower/entry-statement.f90 (+6-6)
  • (modified) flang/test/Lower/forall-pointer-assignment.f90 (+1-1)
  • (modified) flang/test/Lower/forall/scalar-substring.f90 (+1-1)
  • (modified) flang/test/Lower/structure-constructors-alloc-comp.f90 (+3-3)
  • (modified) flang/test/Lower/unsigned-ops.f90 (+4-4)
  • (modified) flang/test/Lower/volatile-derived-type.f90 (+1-1)
  • (modified) flang/test/Lower/volatile-string.f90 (+2-2)
  • (modified) flang/test/Lower/volatile3.f90 (+6-6)
  • (modified) flang/test/Transforms/debug-dummy-argument.fir (+1-1)
  • (modified) flang/test/Transforms/debug-local-var.fir (+6-6)
  • (modified) flang/unittests/Optimizer/FortranVariableTest.cpp (+8-4)
diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index f8322a50effc4..2031f3845509c 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -271,6 +271,12 @@ class AbstractConverter {
   virtual bool
   isRegisteredDummySymbol(Fortran::semantics::SymbolRef symRef) const = 0;
 
+  /// Get the source-level argument position (1-based) for a dummy symbol.
+  /// Returns 0 if the symbol is not a registered dummy or position is unknown.
+  /// Can only be used reliably during the instantiation of variables.
+  virtual unsigned
+  getDummyArgPosition(const Fortran::semantics::Symbol &sym) const = 0;
+
   /// Returns the FunctionLikeUnit being lowered, if any.
   virtual const Fortran::lower::pft::FunctionLikeUnit *
   getCurrentFunctionUnit() const = 0;
diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
index 891373e8dbb0a..9933e3ed6c308 100644
--- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h
+++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
@@ -233,7 +233,7 @@ genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
            fir::FortranVariableFlagsAttr flags,
            mlir::Value dummyScope = nullptr, mlir::Value storage = nullptr,
            std::uint64_t storageOffset = 0,
-           cuf::DataAttributeAttr dataAttr = {});
+           cuf::DataAttributeAttr dataAttr = {}, unsigned dummyArgNo = 0);
 
 /// Generate an hlfir.associate to build a variable from an expression value.
 /// The type of the variable must be provided so that scalar logicals are
diff --git a/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td b/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
index 04f839386498c..b29228eed1591 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
@@ -229,12 +229,13 @@ def fircg_XDeclareOp : fircg_Op<"ext_declare", [AttrSizedOperandSegments]> {
 
   let arguments = (ins AnyRefOrBox:$memref, Variadic<AnyIntegerType>:$shape,
       Variadic<AnyIntegerType>:$shift, Variadic<AnyIntegerType>:$typeparams,
-      Optional<fir_DummyScopeType>:$dummy_scope, Builtin_StringAttr:$uniq_name);
+      Optional<fir_DummyScopeType>:$dummy_scope, Builtin_StringAttr:$uniq_name,
+      OptionalAttr<UI32Attr>:$dummy_arg_no);
   let results = (outs AnyRefOrBox);
 
   let assemblyFormat = [{
     $memref (`(` $shape^ `)`)? (`origin` $shift^)? (`typeparams` $typeparams^)?
-    (`dummy_scope` $dummy_scope^)?
+    (`dummy_scope` $dummy_scope^ (`arg` $dummy_arg_no^)?)?
     attr-dict `:` functional-type(operands, results)
   }];
 
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index bae52d63fda45..da5f7da2f4229 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -3274,13 +3274,14 @@ def fir_DeclareOp
       DefaultValuedAttr<UI64Attr, "0">:$storage_offset,
       Builtin_StringAttr:$uniq_name,
       OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
-      OptionalAttr<cuf_DataAttributeAttr>:$data_attr);
+      OptionalAttr<cuf_DataAttributeAttr>:$data_attr,
+      OptionalAttr<UI32Attr>:$dummy_arg_no);
 
   let results = (outs AnyRefOrBox);
 
   let assemblyFormat = [{
     $memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
-    (`dummy_scope` $dummy_scope^)?
+    (`dummy_scope` $dummy_scope^ (`arg` $dummy_arg_no^)?)?
     (`storage` `(` $storage^ `[` $storage_offset `]` `)`)?
     attr-dict `:` functional-type(operands, results)
   }];
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index b7563a2f752f0..73f6be604a55c 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -103,13 +103,13 @@ def hlfir_DeclareOp
       Builtin_StringAttr:$uniq_name,
       OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
       OptionalAttr<cuf_DataAttributeAttr>:$data_attr,
-      OptionalAttr<UnitAttr>:$skip_rebox);
+      OptionalAttr<UnitAttr>:$skip_rebox, OptionalAttr<UI32Attr>:$dummy_arg_no);
 
   let results = (outs AnyFortranVariable, AnyRefOrBoxLike);
 
   let assemblyFormat = [{
     $memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
-    (`dummy_scope` $dummy_scope^)?
+    (`dummy_scope` $dummy_scope^ (`arg` $dummy_arg_no^)?)?
     (`storage` `(` $storage^ `[` $storage_offset `]` `)`)?
     (`skip_rebox` $skip_rebox^)?
     attr-dict `:` functional-type(operands, results)
@@ -122,7 +122,8 @@ def hlfir_DeclareOp
       CArg<"mlir::Value", "{}">:$storage,
       CArg<"std::uint64_t", "0">:$storage_offset,
       CArg<"fir::FortranVariableFlagsAttr", "{}">:$fortran_attrs,
-      CArg<"cuf::DataAttributeAttr", "{}">:$data_attr)>];
+      CArg<"cuf::DataAttributeAttr", "{}">:$data_attr,
+      CArg<"unsigned", "0">:$dummy_arg_no)>];
 
   let extraClassDeclaration = [{
     /// Get the variable original base (same as input). It lacks
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 5779bcd5d293c..364471eaf9df5 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1129,6 +1129,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     return registeredDummySymbols.contains(sym);
   }
 
+  unsigned getDummyArgPosition(
+      const Fortran::semantics::Symbol &sym) const override final {
+    auto it = dummyArgPositions.find(&sym);
+    return (it != dummyArgPositions.end()) ? it->second : 0;
+  }
+
   const Fortran::lower::pft::FunctionLikeUnit *
   getCurrentFunctionUnit() const override final {
     return currentFunctionUnit;
@@ -1413,11 +1419,14 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   /// definitive mapping. The specification expression have not been lowered
   /// yet. The final mapping will be done using this pre-mapping in
   /// Fortran::lower::mapSymbolAttributes.
+  /// \param argNo The 1-based source position of this argument (0 if
+  /// unknown/result)
   bool mapBlockArgToDummyOrResult(const Fortran::semantics::SymbolRef sym,
-                                  mlir::Value val, bool isResult) {
+                                  mlir::Value val, bool isResult,
+                                  unsigned argNo = 0) {
     localSymbols.addSymbol(sym, val);
     if (!isResult)
-      registerDummySymbol(sym);
+      registerDummySymbol(sym, argNo);
 
     return true;
   }
@@ -5962,7 +5971,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
                             const Fortran::lower::CalleeInterface &callee) {
     assert(builder && "require a builder object at this point");
     using PassBy = Fortran::lower::CalleeInterface::PassEntityBy;
+
+    // Track the source-level argument position (1-based)
+    unsigned argPosition = 0;
+
     auto mapPassedEntity = [&](const auto arg, bool isResult = false) {
+      // Count only actual source-level dummy arguments (not results or
+      // host assoc tuples)
+      if (!isResult && arg.entity.has_value())
+        argPosition++;
+
       if (arg.passBy == PassBy::AddressAndLength) {
         if (callee.characterize().IsBindC())
           return;
@@ -5973,11 +5991,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
         mlir::Value casted =
             builder->createVolatileCast(loc, false, arg.firArgument);
         mlir::Value box = charHelp.createEmboxChar(casted, arg.firLength);
-        mapBlockArgToDummyOrResult(arg.entity->get(), box, isResult);
+        mapBlockArgToDummyOrResult(arg.entity->get(), box, isResult,
+                                   isResult ? 0 : argPosition);
       } else {
         if (arg.entity.has_value()) {
           mapBlockArgToDummyOrResult(arg.entity->get(), arg.firArgument,
-                                     isResult);
+                                     isResult, isResult ? 0 : argPosition);
         } else {
           assert(funit.parentHasTupleHostAssoc() && "expect tuple argument");
         }
@@ -6835,13 +6854,22 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   }
 
   /// Record the given symbol as a dummy argument of this function.
-  void registerDummySymbol(Fortran::semantics::SymbolRef symRef) {
+  /// \param symRef The symbol representing the dummy argument
+  /// \param argNo The 1-based position of this argument in the source (0 =
+  /// unknown)
+  void registerDummySymbol(Fortran::semantics::SymbolRef symRef,
+                           unsigned argNo = 0) {
     auto *sym = &*symRef;
     registeredDummySymbols.insert(sym);
+    if (argNo > 0)
+      dummyArgPositions[sym] = argNo;
   }
 
   /// Reset all registered dummy symbols.
-  void resetRegisteredDummySymbols() { registeredDummySymbols.clear(); }
+  void resetRegisteredDummySymbols() {
+    registeredDummySymbols.clear();
+    dummyArgPositions.clear();
+  }
 
   void setCurrentFunctionUnit(Fortran::lower::pft::FunctionLikeUnit *unit) {
     currentFunctionUnit = unit;
@@ -6883,6 +6911,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 16>
       registeredDummySymbols;
 
+  /// Map from dummy symbols to their 1-based argument positions.
+  /// Used to generate debug info with correct argument numbers.
+  llvm::DenseMap<const Fortran::semantics::Symbol *, unsigned>
+      dummyArgPositions;
+
   /// A map of unique names for constant expressions.
   /// The names are used for representing the constant expressions
   /// with global constant initialized objects.
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 2517ab35d4ff0..53d4d7566acfa 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -1946,12 +1946,15 @@ static void genDeclareSymbol(Fortran::lower::AbstractConverter &converter,
       return;
     }
     mlir::Value dummyScope;
-    if (converter.isRegisteredDummySymbol(sym))
+    unsigned argNo = 0;
+    if (converter.isRegisteredDummySymbol(sym)) {
       dummyScope = converter.dummyArgsScopeValue();
+      argNo = converter.getDummyArgPosition(sym);
+    }
     auto [storage, storageOffset] = converter.getSymbolStorage(sym);
     auto newBase = hlfir::DeclareOp::create(
         builder, loc, base, name, shapeOrShift, lenParams, dummyScope, storage,
-        storageOffset, attributes, dataAttr);
+        storageOffset, attributes, dataAttr, argNo);
     symMap.addVariableDefinition(sym, newBase, force);
     return;
   }
@@ -2004,15 +2007,17 @@ void Fortran::lower::genDeclareSymbol(
                                                         sym.GetUltimate());
     auto name = converter.mangleName(sym);
     mlir::Value dummyScope;
+    unsigned argNo = 0;
     fir::ExtendedValue base = exv;
     if (converter.isRegisteredDummySymbol(sym)) {
       base = genPackArray(converter, sym, exv);
       dummyScope = converter.dummyArgsScopeValue();
+      argNo = converter.getDummyArgPosition(sym);
     }
     auto [storage, storageOffset] = converter.getSymbolStorage(sym);
     hlfir::EntityWithAttributes declare =
         hlfir::genDeclare(loc, builder, base, name, attributes, dummyScope,
-                          storage, storageOffset, dataAttr);
+                          storage, storageOffset, dataAttr, argNo);
     symMap.addVariableDefinition(sym, declare.getIfVariableInterface(), force);
     return;
   }
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index 5da27d1713825..6ef6074cf73ad 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -427,7 +427,8 @@ mlir::Value fir::FirOpBuilder::genTempDeclareOp(
       builder, loc, memref.getType(), memref, shape, typeParams,
       /*dummy_scope=*/nullptr,
       /*storage=*/nullptr,
-      /*storage_offset=*/0, nameAttr, fortranAttrs, cuf::DataAttributeAttr{});
+      /*storage_offset=*/0, nameAttr, fortranAttrs, cuf::DataAttributeAttr{},
+      /*dummy_arg_no=*/mlir::IntegerAttr{});
 }
 
 mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) {
diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
index 793be32400db6..b435ae15cff58 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -250,7 +250,7 @@ hlfir::genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
                   const fir::ExtendedValue &exv, llvm::StringRef name,
                   fir::FortranVariableFlagsAttr flags, mlir::Value dummyScope,
                   mlir::Value storage, std::uint64_t storageOffset,
-                  cuf::DataAttributeAttr dataAttr) {
+                  cuf::DataAttributeAttr dataAttr, unsigned dummyArgNo) {
 
   mlir::Value base = fir::getBase(exv);
   assert(fir::conformsWithPassByRef(base.getType()) &&
@@ -281,7 +281,7 @@ hlfir::genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
       [](const auto &) {});
   auto declareOp = hlfir::DeclareOp::create(
       builder, loc, base, name, shapeOrShift, lenParams, dummyScope, storage,
-      storageOffset, flags, dataAttr);
+      storageOffset, flags, dataAttr, dummyArgNo);
   return mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation());
 }
 
diff --git a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
index 1b1d43c11c707..bafeb32660e6c 100644
--- a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
+++ b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
@@ -302,11 +302,15 @@ class DeclareOpConversion : public mlir::OpRewritePattern<fir::DeclareOp> {
       else
         return mlir::failure();
     }
+    // Extract dummy_arg_no attribute if present
+    mlir::IntegerAttr dummyArgNoAttr;
+    if (auto attr = declareOp->getAttrOfType<mlir::IntegerAttr>("dummy_arg_no"))
+      dummyArgNoAttr = attr;
     // FIXME: Add FortranAttrs and CudaAttrs
     auto xDeclOp = fir::cg::XDeclareOp::create(
         rewriter, loc, declareOp.getType(), declareOp.getMemref(), shapeOpers,
         shiftOpers, declareOp.getTypeparams(), declareOp.getDummyScope(),
-        declareOp.getUniqName());
+        declareOp.getUniqName(), dummyArgNoAttr);
     LLVM_DEBUG(llvm::dbgs()
                << "rewriting " << declareOp << " to " << xDeclOp << '\n');
     rewriter.replaceOp(declareOp, xDeclOp.getOperation()->getResults());
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 1332dc57fb086..8d6b888789c15 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -261,14 +261,12 @@ updateDeclaredInputTypeWithVolatility(mlir::Type inputType, mlir::Value memref,
   return std::make_pair(inputType, memref);
 }
 
-void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
-                             mlir::OperationState &result, mlir::Value memref,
-                             llvm::StringRef uniq_name, mlir::Value shape,
-                             mlir::ValueRange typeparams,
-                             mlir::Value dummy_scope, mlir::Value storage,
-                             std::uint64_t storage_offset,
-                             fir::FortranVariableFlagsAttr fortran_attrs,
-                             cuf::DataAttributeAttr data_attr) {
+void hlfir::DeclareOp::build(
+    mlir::OpBuilder &builder, mlir::OperationState &result, mlir::Value memref,
+    llvm::StringRef uniq_name, mlir::Value shape, mlir::ValueRange typeparams,
+    mlir::Value dummy_scope, mlir::Value storage, std::uint64_t storage_offset,
+    fir::FortranVariableFlagsAttr fortran_attrs,
+    cuf::DataAttributeAttr data_attr, unsigned dummy_arg_no) {
   auto nameAttr = builder.getStringAttr(uniq_name);
   mlir::Type inputType = memref.getType();
   bool hasExplicitLbs = hasExplicitLowerBounds(shape);
@@ -279,9 +277,12 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
   }
   auto [hlfirVariableType, firVarType] =
       getDeclareOutputTypes(inputType, hasExplicitLbs);
+  mlir::IntegerAttr argNoAttr;
+  if (dummy_arg_no > 0)
+    argNoAttr = builder.getUI32IntegerAttr(dummy_arg_no);
   build(builder, result, {hlfirVariableType, firVarType}, memref, shape,
         typeparams, dummy_scope, storage, storage_offset, nameAttr,
-        fortran_attrs, data_attr, /*skip_rebox=*/mlir::UnitAttr{});
+        fortran_attrs, data_attr, /*skip_rebox=*/mlir::UnitAttr{}, argNoAttr);
 }
 
 llvm::LogicalResult hlfir::DeclareOp::verify() {
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index 6a57bf2ae6fec..4c3f37bdead3f 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -308,7 +308,8 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
         declareOp.getTypeparams(), declareOp.getDummyScope(),
         /*storage=*/declareOp.getStorage(),
         /*storage_offset=*/declareOp.getStorageOffset(),
-        declareOp.getUniqName(), fortranAttrs, dataAttr);
+        declareOp.getUniqName(), fortranAttrs, dataAttr,
+        declareOp.getDummyArgNoAttr());
 
     // Propagate other attributes from hlfir.declare to fir.declare.
     // OpenACC's acc.declare is one example. Right now, the propagation
diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index e006d2e878fd8..00633b4104b6c 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -228,24 +228,11 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
     }
   }
 
-  // FIXME: There may be cases where an argument is processed a bit before
-  // DeclareOp is generated. In that case, DeclareOp may point to an
-  // intermediate op and not to BlockArgument.
-  // Moreover, with MLIR inlining we cannot use the BlockArgument
-  // position to identify the original number of the dummy argument.
-  // If we want to keep running AddDebugInfoPass late, the dummy argument
-  // position in the argument list has to be expressed in FIR (e.g. as a
-  // constant attribute of [hl]fir.declare/fircg.ext_declare operation that has
-  // a dummy_scope operand).
+  // Get the dummy argument position from the explicit attribute.
   unsigned argNo = 0;
   if (declOp.getDummyScope()) {
-    if (auto arg = llvm::dyn_cast<mlir::BlockArgument>(declOp.getMemref())) {
-      // Check if it is the BlockArgument of the function's entry block.
-      if (auto funcLikeOp =
-              declOp->getParentOfType<mlir::FunctionOpInterface>())
-        if (arg.getOwner() == &funcLikeOp.front())
-          argNo = arg.getArgNumber() + 1;
-    }
+    if (auto argNoOpt = declOp.getDummyArgNo())
+      argNo = *argNoOpt;
   }
 
   auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()),
diff --git a/flang/test/Fir/dispatch.f90 b/flang/test/Fir/dispatch.f90
index 2b1ae225986ca..9219e5453c779 100644
--- a/flang/test/Fir/dispatch.f90
+++ b/flang/test/Fir/dispatch.f90
@@ -195,7 +195,7 @@ program test_type_to_class
 
 ! CHECK-LABEL: func.func @_QMdispatch1Pdisplay_class(
 ! CHECK-SAME: %[[ARG:.*]]: [[CLASS:!fir.class<.*>>]]
-! CHECK: %[[ARG_DECL:.*]]:2 = hlfir.declare %[[ARG]] dummy_scope %{{[0-9]+}} {uniq_name = "_QMdispatch1Fdisplay_classEp"} : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>, !fir.dscope) -> (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>, !fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>)
+! CHECK: %[[ARG_DECL:.*]]:2 = hlfir.declare %[[ARG]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {uniq_name = "_QMdispatch1Fdisplay_classEp"} : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>, !fir.dscope) -> (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>, !fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>)
 
 ! Check dynamic dispatch equal to `call p%display2()` with binding index = 2.
 ! CHECK: %[[BOXDESC:.*]] = fir.box_tdesc %[[ARG_DECL]]#0 : ([[CLASS]]) -> !fir.tdesc<none>
diff --git a/flang/test/HLFIR/assumed-type-actual-args.f90 b/flang/test/HLFIR/assumed-type-actual-args.f90
index aaac98...
[truncated]

Copy link
Contributor

@tblah tblah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for taking the time to update so many tests!

CHARACTER dummy arguments were treated as local variable because our
method to get the argument number relied on DeclareOp having a direct
reference to arguments which was not the case for character argument.
This is fixed by storing source-level argument positions in DeclareOp.

Fixes llvm#112886
@abidh abidh force-pushed the flang-debug-dummy-arg-positions branch from 2fb0246 to 007d294 Compare November 11, 2025 12:11
@abidh
Copy link
Contributor Author

abidh commented Nov 11, 2025

The CI was failing because of a recently added test. I have updated the test now.

@abidh abidh merged commit cfc56c9 into llvm:main Nov 12, 2025
11 checks passed
git-crd pushed a commit to git-crd/crd-llvm-project that referenced this pull request Nov 13, 2025
CHARACTER dummy arguments were treated as local variables in debug info.
This happened because our method to get the argument number was not
robust. It relied on `DeclareOp` having a direct reference to arguments
which was not the case for character arguments. This is fixed by storing
source-level argument positions in `DeclareOp`.

Fixes llvm#112886
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flang:codegen flang:fir-hlfir flang:openmp flang Flang issues not falling into any other category openacc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[flang][debug] Wrong function signature with CHARACTER arguments.

3 participants