[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-10-18 Thread Michael Buch via cfe-commits

Michael137 wrote:

> I'm afraid I'm seeing a test regression with this change (confirmed via 
> bisect), on Gentoo amd64:
> 
> ```
> FAIL: Clang :: DebugInfo/CXX/structured-binding.cpp (11221 of 22437)
>  TEST 'Clang :: DebugInfo/CXX/structured-binding.cpp' 
> FAILED 
> Exit Code: 1
> 
> Command Output (stderr):
> --
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/clang
>  -cc1 -internal-isystem 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/../../../../lib/clang/22/include
>  -nostdsysteminc -std=c++23 -emit-llvm -debug-info-kind=standalone -triple 
> i686-pc-linux-gnu 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  -o - | /usr/lib/llvm/22/bin/FileCheck 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  --implicit-check-not="call void @llvm.dbg.declare" # RUN: at line 1
> + 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/clang
>  -cc1 -internal-isystem 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/../../../../lib/clang/22/include
>  -nostdsysteminc -std=c++23 -emit-llvm -debug-info-kind=standalone -triple 
> i686-pc-linux-gnu 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  -o -
> + /usr/lib/llvm/22/bin/FileCheck 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  '--implicit-check-not=call void @llvm.dbg.declare'
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp:12:11:
>  error: CHECK: expected string not found in input
> // CHECK: #dbg_declare(ptr %v, ![[VAR_8:[0-9]+]], !DIExpression()
>   ^
> :152:44: note: scanning from here
>  #dbg_declare(ptr %k, !133, !DIExpression(), !139)
>^
> :163:2: note: possible intended match here
>  #dbg_declare(ptr %v6, !140, !DIExpression(), !145)
>  ^
> 
> Input file: 
> Check file: 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
> 
> -dump-input=help explains the following input dump.
> 
> Input was:
> <<
> .
> .
> .
>   147:  store <2 x i32> , ptr %vctr, align 8, !dbg !126 
>   148:  #dbg_declare(ptr %5, !127, !DIExpression(DW_OP_deref), !128) 
>   149:  #dbg_declare(ptr %5, !129, !DIExpression(DW_OP_deref, 
> DW_OP_plus_uconst, 4), !130) 
>   150:  store ptr %vctr, ptr %5, align 4, !dbg !131 
>   151:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %6, ptr align 4 
> @__const._Z1fv..1, i32 12, i1 false), !dbg !132 
>   152:  #dbg_declare(ptr %k, !133, !DIExpression(), !139) 
> check:12'0X~~~ error: no 
> match found
>   153:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr 
> align 4 %6, i32 12, i1 false), !dbg !139 
> check:12'0 
> ~
>   154:  %k4 = getelementptr inbounds nuw %"struct.std::triple", ptr 
> %agg.tmp, i32 0, i32 0, !dbg !139 
> check:12'0 
> ~~~
>   155:  %9 = load i32, ptr %k4, align 4, !dbg !139 
> check:12'0 
>   156:  %v = getelementptr inbounds nuw %"struct.std::triple", ptr 
> %agg.tmp, i32 0, i32 1, !dbg !139 
> check:12'0 
> ~~
>   157:  %10 = load i32, ptr %v, align 4, !dbg !139 
> check:12'0 
>   158:  %w = getelementptr inbounds nuw %"struct.std::triple", ptr 
> %agg.tmp, i32 0, i32 2, !dbg !139 
> check:12'0 
> ~~
>   159:  %11 = load i32, ptr %w, align 4, !dbg !139 
> check:12'0 
>   160:  %call5 = call noundef i32 @_ZSt3getILj0EEiSt6triple(i32 %9, 
> i32 %10, i32 %11), !dbg !139 
> check:12'0 
> ~~
>   161:  store i32 %call5, ptr %ref.tmp3, align 4, !dbg !139 
> check:12'0 ~
>   162:  store ptr %ref.tmp3, ptr %k, align 4, !dbg !139 
> check:12'0 ~
>   163:  #dbg_declare(ptr %v6, !140, !DIExpression(), !145) 
> check:12'0 
> check:12'1  ?   

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-10-18 Thread Michał Górny via cfe-commits

mgorny wrote:

Sure:

```llvm
; ModuleID = 
'/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp'
source_filename = 
"/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp"
target datalayout = 
"e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686-pc-linux-gnu"

%struct.A = type { i32, i32 }
%struct.B = type { i32, i32 }
%"struct.std::triple" = type { i32, i32, i32 }
%struct.C = type { i32, i32 }
%struct.D = type { i32, i32 }

$_ZN1B3getILi0EEEiv = comdat any

$_ZN1B3getILi1EEEiv = comdat any

$_ZNH1C3getILi0EEEiOS_ = comdat any

$_ZNH1C3getILi1EEEiOS_ = comdat any

$_ZN1D3getILi0EEEii = comdat any

$_ZN1D3getILi1EEEii = comdat any

@__const._Z1fv.a = private unnamed_addr constant %struct.A { i32 10, i32 20 }, 
align 4
@__const._Z1fv. = private unnamed_addr constant %struct.B { i32 1, i32 2 }, 
align 4
@__const._Z1fv.array = private unnamed_addr constant [2 x i32] [i32 3, i32 4], 
align 4
@__const._Z1fv..1 = private unnamed_addr constant %"struct.std::triple" { i32 
3, i32 4, i32 5 }, align 4
@__const._Z1fv..2 = private unnamed_addr constant %struct.C { i32 2, i32 3 }, 
align 4
@__const._Z1fv..3 = private unnamed_addr constant %struct.D { i32 2, i32 3 }, 
align 4

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_ZSt3getILj0EEiSt6triple(i32 %p.0, i32 %p.1, i32 
%p.2) #0 !dbg !46 {
entry:
  %p = alloca %"struct.std::triple", align 4
  %k = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0
  store i32 %p.0, ptr %k, align 4
  %v = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1
  store i32 %p.1, ptr %v, align 4
  %w = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2
  store i32 %p.2, ptr %w, align 4
#dbg_declare(ptr %p, !53, !DIExpression(), !54)
  %k1 = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0, 
!dbg !55
  %0 = load i32, ptr %k1, align 4, !dbg !55
  ret i32 %0, !dbg !56
}

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_ZSt3getILj1EEiSt6triple(i32 %p.0, i32 %p.1, i32 
%p.2) #0 !dbg !57 {
entry:
  %p = alloca %"struct.std::triple", align 4
  %k = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0
  store i32 %p.0, ptr %k, align 4
  %v = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1
  store i32 %p.1, ptr %v, align 4
  %w = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2
  store i32 %p.2, ptr %w, align 4
#dbg_declare(ptr %p, !60, !DIExpression(), !61)
  %v1 = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1, 
!dbg !62
  %0 = load i32, ptr %v1, align 4, !dbg !62
  ret i32 %0, !dbg !63
}

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_ZSt3getILj2EEiSt6triple(i32 %p.0, i32 %p.1, i32 
%p.2) #0 !dbg !64 {
entry:
  %p = alloca %"struct.std::triple", align 4
  %k = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0
  store i32 %p.0, ptr %k, align 4
  %v = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1
  store i32 %p.1, ptr %v, align 4
  %w = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2
  store i32 %p.2, ptr %w, align 4
#dbg_declare(ptr %p, !67, !DIExpression(), !68)
  %w1 = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2, 
!dbg !69
  %0 = load i32, ptr %w1, align 4, !dbg !69
  ret i32 %0, !dbg !70
}

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_Z1fv() #0 !dbg !71 {
entry:
  %a = alloca %struct.A, align 4
  %0 = alloca %struct.A, align 4
  %1 = alloca ptr, align 4
  %2 = alloca %struct.B, align 4
  %z1 = alloca ptr, align 4
  %ref.tmp = alloca i32, align 4
  %z2 = alloca ptr, align 4
  %ref.tmp1 = alloca i32, align 4
  %array = alloca [2 x i32], align 4
  %3 = alloca ptr, align 4
  %cmplx = alloca { i32, i32 }, align 4
  %4 = alloca ptr, align 4
  %vctr = alloca <2 x i32>, align 8
  %5 = alloca ptr, align 4
  %6 = alloca %"struct.std::triple", align 4
  %k = alloca ptr, align 4
  %ref.tmp3 = alloca i32, align 4
  %agg.tmp = alloca %"struct.std::triple", align 4
  %v6 = alloca ptr, align 4
  %ref.tmp7 = alloca i32, align 4
  %agg.tmp8 = alloca %"struct.std::triple", align 4
  %w13 = alloca ptr, align 4
  %ref.tmp14 = alloca i32, align 4
  %agg.tmp15 = alloca %"struct.std::triple", align 4
  %7 = alloca %struct.C, align 4
  %m = alloca ptr, align 4
  %ref.tmp20 = alloca i32, align 4
  %n = alloca ptr, align 4
  %ref.tmp22 = alloca i32, align 4
  %8 = alloca %struct.D, align 4
  %s = alloca ptr, align 4
  %ref.tmp24 = alloca i32, align 4
  %p = alloca ptr, align 4
  %ref.tmp26 = alloca i32, align 4
#dbg_declare(ptr %a, !74, !DIExpression(), !79)
  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %a, ptr align 4 
@__const._Z1fv.a, i3

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-10-18 Thread Michael Buch via cfe-commits

Michael137 wrote:

@mgorny Any chance you have access to the full IR when compiling it with the 
clang on that bot?

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-10-18 Thread Michael Buch via cfe-commits

Michael137 wrote:

> I'm afraid I'm seeing a test regression with this change (confirmed via 
> bisect), on Gentoo amd64:
> 
> ```
> FAIL: Clang :: DebugInfo/CXX/structured-binding.cpp (11221 of 22437)
>  TEST 'Clang :: DebugInfo/CXX/structured-binding.cpp' 
> FAILED 
> Exit Code: 1
> 
> Command Output (stderr):
> --
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/clang
>  -cc1 -internal-isystem 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/../../../../lib/clang/22/include
>  -nostdsysteminc -std=c++23 -emit-llvm -debug-info-kind=standalone -triple 
> i686-pc-linux-gnu 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  -o - | /usr/lib/llvm/22/bin/FileCheck 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  --implicit-check-not="call void @llvm.dbg.declare" # RUN: at line 1
> + 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/clang
>  -cc1 -internal-isystem 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/../../../../lib/clang/22/include
>  -nostdsysteminc -std=c++23 -emit-llvm -debug-info-kind=standalone -triple 
> i686-pc-linux-gnu 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  -o -
> + /usr/lib/llvm/22/bin/FileCheck 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
>  '--implicit-check-not=call void @llvm.dbg.declare'
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp:12:11:
>  error: CHECK: expected string not found in input
> // CHECK: #dbg_declare(ptr %v, ![[VAR_8:[0-9]+]], !DIExpression()
>   ^
> :152:44: note: scanning from here
>  #dbg_declare(ptr %k, !133, !DIExpression(), !139)
>^
> :163:2: note: possible intended match here
>  #dbg_declare(ptr %v6, !140, !DIExpression(), !145)
>  ^
> 
> Input file: 
> Check file: 
> /var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
> 
> -dump-input=help explains the following input dump.
> 
> Input was:
> <<
> .
> .
> .
>   147:  store <2 x i32> , ptr %vctr, align 8, !dbg !126 
>   148:  #dbg_declare(ptr %5, !127, !DIExpression(DW_OP_deref), !128) 
>   149:  #dbg_declare(ptr %5, !129, !DIExpression(DW_OP_deref, 
> DW_OP_plus_uconst, 4), !130) 
>   150:  store ptr %vctr, ptr %5, align 4, !dbg !131 
>   151:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %6, ptr align 4 
> @__const._Z1fv..1, i32 12, i1 false), !dbg !132 
>   152:  #dbg_declare(ptr %k, !133, !DIExpression(), !139) 
> check:12'0X~~~ error: no 
> match found
>   153:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr 
> align 4 %6, i32 12, i1 false), !dbg !139 
> check:12'0 
> ~
>   154:  %k4 = getelementptr inbounds nuw %"struct.std::triple", ptr 
> %agg.tmp, i32 0, i32 0, !dbg !139 
> check:12'0 
> ~~~
>   155:  %9 = load i32, ptr %k4, align 4, !dbg !139 
> check:12'0 
>   156:  %v = getelementptr inbounds nuw %"struct.std::triple", ptr 
> %agg.tmp, i32 0, i32 1, !dbg !139 
> check:12'0 
> ~~
>   157:  %10 = load i32, ptr %v, align 4, !dbg !139 
> check:12'0 
>   158:  %w = getelementptr inbounds nuw %"struct.std::triple", ptr 
> %agg.tmp, i32 0, i32 2, !dbg !139 
> check:12'0 
> ~~
>   159:  %11 = load i32, ptr %w, align 4, !dbg !139 
> check:12'0 
>   160:  %call5 = call noundef i32 @_ZSt3getILj0EEiSt6triple(i32 %9, 
> i32 %10, i32 %11), !dbg !139 
> check:12'0 
> ~~
>   161:  store i32 %call5, ptr %ref.tmp3, align 4, !dbg !139 
> check:12'0 ~
>   162:  store ptr %ref.tmp3, ptr %k, align 4, !dbg !139 
> check:12'0 ~
>   163:  #dbg_declare(ptr %v6, !140, !DIExpression(), !145) 
> check:12'0 
> check:12'1  ?   

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-23 Thread Michał Górny via cfe-commits

mgorny wrote:

I'm afraid I'm seeing a test regression with this change (confirmed via 
bisect), on Gentoo amd64:

```
FAIL: Clang :: DebugInfo/CXX/structured-binding.cpp (11221 of 22437)
 TEST 'Clang :: DebugInfo/CXX/structured-binding.cpp' 
FAILED 
Exit Code: 1

Command Output (stderr):
--
/var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/clang
 -cc1 -internal-isystem 
/var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/../../../../lib/clang/22/include
 -nostdsysteminc -std=c++23 -emit-llvm -debug-info-kind=standalone -triple 
i686-pc-linux-gnu 
/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
 -o - | /usr/lib/llvm/22/bin/FileCheck 
/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
 --implicit-check-not="call void @llvm.dbg.declare" # RUN: at line 1
+ 
/var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/clang
 -cc1 -internal-isystem 
/var/tmp/portage/llvm-core/clang-22.0.0./work/x/y/clang-abi_x86_32.x86/bin/../../../../lib/clang/22/include
 -nostdsysteminc -std=c++23 -emit-llvm -debug-info-kind=standalone -triple 
i686-pc-linux-gnu 
/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
 -o -
+ /usr/lib/llvm/22/bin/FileCheck 
/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp
 '--implicit-check-not=call void @llvm.dbg.declare'
/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp:12:11:
 error: CHECK: expected string not found in input
// CHECK: #dbg_declare(ptr %v, ![[VAR_8:[0-9]+]], !DIExpression()
  ^
:152:44: note: scanning from here
 #dbg_declare(ptr %k, !133, !DIExpression(), !139)
   ^
:163:2: note: possible intended match here
 #dbg_declare(ptr %v6, !140, !DIExpression(), !145)
 ^

Input file: 
Check file: 
/var/tmp/portage/llvm-core/clang-22.0.0./work/clang/test/DebugInfo/CXX/structured-binding.cpp

-dump-input=help explains the following input dump.

Input was:
<<
.
.
.
  147:  store <2 x i32> , ptr %vctr, align 8, !dbg !126 
  148:  #dbg_declare(ptr %5, !127, !DIExpression(DW_OP_deref), !128) 
  149:  #dbg_declare(ptr %5, !129, !DIExpression(DW_OP_deref, 
DW_OP_plus_uconst, 4), !130) 
  150:  store ptr %vctr, ptr %5, align 4, !dbg !131 
  151:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %6, ptr align 4 
@__const._Z1fv..1, i32 12, i1 false), !dbg !132 
  152:  #dbg_declare(ptr %k, !133, !DIExpression(), !139) 
check:12'0X~~~ error: no 
match found
  153:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr 
align 4 %6, i32 12, i1 false), !dbg !139 
check:12'0 
~
  154:  %k4 = getelementptr inbounds nuw %"struct.std::triple", ptr 
%agg.tmp, i32 0, i32 0, !dbg !139 
check:12'0 
~~~
  155:  %9 = load i32, ptr %k4, align 4, !dbg !139 
check:12'0 
  156:  %v = getelementptr inbounds nuw %"struct.std::triple", ptr 
%agg.tmp, i32 0, i32 1, !dbg !139 
check:12'0 
~~
  157:  %10 = load i32, ptr %v, align 4, !dbg !139 
check:12'0 
  158:  %w = getelementptr inbounds nuw %"struct.std::triple", ptr 
%agg.tmp, i32 0, i32 2, !dbg !139 
check:12'0 
~~
  159:  %11 = load i32, ptr %w, align 4, !dbg !139 
check:12'0 
  160:  %call5 = call noundef i32 @_ZSt3getILj0EEiSt6triple(i32 %9, i32 
%10, i32 %11), !dbg !139 
check:12'0 
~~
  161:  store i32 %call5, ptr %ref.tmp3, align 4, !dbg !139 
check:12'0 ~
  162:  store ptr %ref.tmp3, ptr %k, align 4, !dbg !139 
check:12'0 ~
  163:  #dbg_declare(ptr %v6, !140, !DIExpression(), !145) 
check:12'0 
check:12'1  ?   possible 
intended match
  164:  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp8, ptr 
align 4 %6, i32 12, i1 false), !dbg !145 
check:12'0 

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-20 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 01/14] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -1

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-20 Thread LLVM Continuous Integration via cfe-commits

llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `sanitizer-aarch64-linux` 
running on `sanitizer-buildbot8` while building `clang,lldb` at step 2 
"annotate".

Full details are available at: 
https://lab.llvm.org/buildbot/#/builders/51/builds/23865


Here is the relevant piece of the build log for the reference

```
Step 2 (annotate) failure: 'python 
../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py'
 (failure)
...
[182/186] Generating MSAN_INST_TEST_OBJECTS.msan_test.cpp.aarch64-with-call.o
[183/186] Generating Msan-aarch64-with-call-Test
[184/186] Generating MSAN_INST_TEST_OBJECTS.msan_test.cpp.aarch64.o
[185/186] Generating Msan-aarch64-Test
[185/186] Running compiler_rt regression tests
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/discovery.py:276:
 warning: input 
'/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/interception/Unit'
 contained no tests
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/discovery.py:276:
 warning: input 
'/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/Unit'
 contained no tests
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/main.py:74:
 note: The test suite configuration requested an individual test timeout of 0 
seconds but a timeout of 900 seconds was requested on the command line. Forcing 
timeout to be 900 seconds.
-- Testing: 5972 tests, 72 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.
FAIL: libFuzzer-aarch64-libcxx-Linux :: reduce_inputs.test (5747 of 5972)
 TEST 'libFuzzer-aarch64-libcxx-Linux :: 
reduce_inputs.test' FAILED 
Exit Code: 1

Command Output (stderr):
--
rm -rf 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp/C
 # RUN: at line 3
+ rm -rf 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp/C
mkdir -p 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp/C
 # RUN: at line 4
+ mkdir -p 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp/C
/home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   
--driver-mode=g++ -O2 -gline-tables-only -fsanitize=address,fuzzer 
-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/lib/fuzzer  
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta  
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/fuzzer/ShrinkControlFlowSimpleTest.cpp
 -o 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp-ShrinkControlFlowSimpleTest
 # RUN: at line 5
+ /home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang 
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta 
--driver-mode=g++ -O2 -gline-tables-only -fsanitize=address,fuzzer 
-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/lib/fuzzer 
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/fuzzer/ShrinkControlFlowSimpleTest.cpp
 -o 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp-ShrinkControlFlowSimpleTest
/home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   
--driver-mode=g++ -O2 -gline-tables-only -fsanitize=address,fuzzer 
-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/lib/fuzzer  
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta  
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/fuzzer/ShrinkControlFlowTest.cpp
 -o 
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64LibcxxLinuxConfig/Output/reduce_inputs.test.tmp-ShrinkControlFlowTest
 # RUN: at line 6
+ /home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang 
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta 
--driver-mode=g++ -O2 -gline-tables-only -fsanitize=address,fuzzer 
-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/lib/fuzzer 
-Wthread-safety -Wthread-safety-reference -Wthread-safety-beta 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/fuzzer/ShrinkControlFlowTest.cpp
 -o 
/home/b/sanitizer-aarch64-linux/build/build_default/run

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-20 Thread Michael Buch via cfe-commits

https://github.com/Michael137 closed 
https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-20 Thread Corentin Jabot via cfe-commits

https://github.com/cor3ntin approved this pull request.


https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-20 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 01/14] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -1

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-20 Thread Michael Buch via cfe-commits

Michael137 wrote:

> Just as few nits (sorry for the delay and thanks for the pings!)

No worries! Thanks for the review

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits

https://github.com/cor3ntin edited 
https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits


@@ -2544,6 +2544,19 @@ Stmt *BlockExpr::getBody() {
 
//===--===//
 // Generic Expression Routines
 
//===--===//
+namespace {
+/// Helper to determine wether \c E is a CXXConstructExpr constructing
+/// a DecompositionDecl. Used to skip Clang-generated calls to std::get
+/// for structured bindings.
+bool IsDecompositionDeclRefExpr(const Expr *E) {
+  const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource();

cor3ntin wrote:

```suggestion
  const Expr *Unwrapped = E->IgnoreUnlessSpelledInSource();
```

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits


@@ -2544,6 +2544,19 @@ Stmt *BlockExpr::getBody() {
 
//===--===//
 // Generic Expression Routines
 
//===--===//
+namespace {
+/// Helper to determine wether \c E is a CXXConstructExpr constructing
+/// a DecompositionDecl. Used to skip Clang-generated calls to std::get
+/// for structured bindings.
+bool IsDecompositionDeclRefExpr(const Expr *E) {
+  const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource();
+  const DeclRefExpr *Ref = llvm::dyn_cast_or_null(Unrwapped);
+  if (!Ref)
+return false;
+
+  return llvm::isa_and_nonnull(Ref->getDecl());

cor3ntin wrote:

```suggestion
  return isa_and_nonnull(Ref->getDecl());
```

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits

https://github.com/cor3ntin commented:

Just as few nits (sorry for the delay and thanks for the pings!)

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits


@@ -2544,6 +2544,19 @@ Stmt *BlockExpr::getBody() {
 
//===--===//
 // Generic Expression Routines
 
//===--===//
+namespace {
+/// Helper to determine wether \c E is a CXXConstructExpr constructing
+/// a DecompositionDecl. Used to skip Clang-generated calls to std::get
+/// for structured bindings.
+bool IsDecompositionDeclRefExpr(const Expr *E) {
+  const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource();
+  const DeclRefExpr *Ref = llvm::dyn_cast_or_null(Unrwapped);

cor3ntin wrote:

```suggestion
  const auto *Ref = dyn_cast_or_null(Unrwapped);
```

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits


@@ -2544,6 +2544,19 @@ Stmt *BlockExpr::getBody() {
 
//===--===//
 // Generic Expression Routines
 
//===--===//
+namespace {
+/// Helper to determine wether \c E is a CXXConstructExpr constructing
+/// a DecompositionDecl. Used to skip Clang-generated calls to std::get
+/// for structured bindings.
+bool IsDecompositionDeclRefExpr(const Expr *E) {
+  const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource();
+  const DeclRefExpr *Ref = llvm::dyn_cast_or_null(Unrwapped);

cor3ntin wrote:

I don't think E can be null, so Unwrapped probably cannot be null either, it 
should just be `dyn_cast`

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Corentin Jabot via cfe-commits


@@ -2544,6 +2544,19 @@ Stmt *BlockExpr::getBody() {
 
//===--===//
 // Generic Expression Routines
 
//===--===//
+namespace {
+/// Helper to determine wether \c E is a CXXConstructExpr constructing
+/// a DecompositionDecl. Used to skip Clang-generated calls to std::get
+/// for structured bindings.
+bool IsDecompositionDeclRefExpr(const Expr *E) {

cor3ntin wrote:

I'd use static rather than an anonymous namespace here

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-19 Thread Michael Buch via cfe-commits

Michael137 wrote:

any objections to latest version @cor3ntin ?

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-13 Thread Michael Buch via cfe-commits

Michael137 wrote:

gentle ping

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-06 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 01/10] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -1

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-04 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 01/11] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -1

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-04 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 01/13] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -1

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-04 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 01/12] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -1

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-04 Thread Michael Buch via cfe-commits

Michael137 wrote:

Latest commit narrows the heuristics so it specifically applies to `CallExpr`s 
generated for structured bindings. Here's what the AST looks like when stopped 
in `IgnoreImplicitCallSingleStep` for a structured binding:

```
(lldb) p E->dump()  

 
CallExpr 0x912dd6cb8 'int' adl  

 
|-ImplicitCastExpr 0x912dd6ca0 'int (*)(triple)'

 
| `-DeclRefExpr 0x912dd6be8 'int (triple)' lvalue Function 0x912dbdb98 'get' 
'int (triple)' (FunctionTemplate 0x912dbd990 'get') 

`-CXXConstructExpr 0x912dd80e0 'triple' 'void (triple &&) noexcept' 

 
  `-ImplicitCastExpr 0x912dd64b0 'std::triple' xvalue 

 
`-DeclRefExpr 0x912dd6490 'std::triple' lvalue Decomposition 0x912dd4f68 
first_binding 'k' 'std::triple' 


(lldb) p A->dump()  

 
CXXConstructExpr 0x912dd80e0 'triple' 'void (triple &&) noexcept'   

 
`-ImplicitCastExpr 0x912dd64b0 'std::triple' xvalue   

 
  `-DeclRefExpr 0x912dd6490 'std::triple' lvalue Decomposition 0x912dd4f68 
first_binding 'k' 'std::triple' 
  
```

What my latest change does is try and get to that `DeclRefExpr 0x912dd6490 
'std::triple' lvalue Decomposition` node. And only for those cases ignore the 
`CallExpr`.

Let me know what you think @cor3ntin @AaronBallman. There might be a better way 
of doing this. Particularly, I wasn't sure how to unwrap the `DeclRefExpr`, so 
I just used `IgnoreUnlessSpelledInSource` again (recursively).

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-03 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 1/9] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -13,

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-03 Thread Michael Buch via cfe-commits

Michael137 wrote:

> I'm still happy with this. I did not notice it was not merged

Yea I was just clearing out my open PRs and noticed this was still open. There 
is still that clang-tidy failure I haven't figured out. Might need a more 
targeted heuristic for expressions coming from binding decls.

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-03 Thread Corentin Jabot via cfe-commits

cor3ntin wrote:

I'm still happy with this. I did not notice it was not merged

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-09-03 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From a9e13ad8d2a7a95d431dddcced611bea1e83b99a Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 1/8] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../test/DebugInfo/CXX/structured-binding.cpp | 27 +++
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0385dbdac869b..4b0f2bce457df 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -87,12 +87,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/DebugInfo/CXX/structured-binding.cpp 
b/clang/test/DebugInfo/CXX/structured-binding.cpp
index 8032ce85c9e25..7d4919ad52bc7 100644
--- a/clang/test/DebugInfo/CXX/structured-binding.cpp
+++ b/clang/test/DebugInfo/CXX/structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpression(DW_OP_plus_uconst, 4),
@@ -13,

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-17 Thread Michael Buch via cfe-commits

Michael137 wrote:

Hmm there's a clang-tidy test failing because this codepath gets hit for 
`UserDefinedLiteral`, which is a `CallExpr` with a single argument:
```
(lldb) p E->dump()
UserDefinedLiteral 0x15402b738 'unsigned long long'
|-ImplicitCastExpr 0x15402b720 'unsigned long long (*)(unsigned long long)' 

| `-DeclRefExpr 0x15402b6c8 'unsigned long long (unsigned long long)' lvalue 
Function 0x154010278 'operator""_ull' 'unsigned long long (unsigned long long)'
`-IntegerLiteral 0x15402b6a8 'unsigned long long' 1
(lldb) p E->getSourceRange()
(clang::SourceRange) {
  B = (ID = 99)
  E = (ID = 99)
}
(lldb) p A->getSourceRange()
(clang::SourceRange) {
  B = (ID = 99)
  E = (ID = 99)
}
```
Adding a special case for `UserDefinedLiteral` doesn't seem like the right 
thing to do. Trying to wrap my head around what clang-tidy is doing here

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-16 Thread Michael Buch via cfe-commits


@@ -3183,10 +3183,24 @@ Expr *Expr::IgnoreUnlessSpelledInSource() {
 }
 return E;
   };
+
+  auto IgnoreImplicitCallSingleStep = [](Expr *E) {
+if (auto *C = dyn_cast(E)) {
+  auto NumArgs = C->getNumArgs();
+  if (NumArgs == 1 ||
+  (NumArgs > 1 && isa(C->getArg(1 {
+Expr *A = C->getArg(0);
+if (A->getSourceRange() == E->getSourceRange())
+  return A;
+  }
+}
+return E;
+  };
+

Michael137 wrote:

done

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-16 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From b43ffd0c1bb6e4f1ca5b8458848f574480021b08 Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 1/8] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../debug-info-structured-binding.cpp | 28 ++-
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index d7e5e95b7873a0..f49e759e50ea3d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -85,12 +85,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp 
b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index 5fbd54c16382c0..55a84a65015842 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpre

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-16 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From b43ffd0c1bb6e4f1ca5b8458848f574480021b08 Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 1/8] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../debug-info-structured-binding.cpp | 28 ++-
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index d7e5e95b7873a0..f49e759e50ea3d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -85,12 +85,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp 
b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index 5fbd54c16382c0..55a84a65015842 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpre

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-16 Thread via cfe-commits

https://github.com/cor3ntin approved this pull request.

LGTM modulo comment request.
Thanks

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-16 Thread via cfe-commits


@@ -3183,10 +3183,24 @@ Expr *Expr::IgnoreUnlessSpelledInSource() {
 }
 return E;
   };
+
+  auto IgnoreImplicitCallSingleStep = [](Expr *E) {
+if (auto *C = dyn_cast(E)) {
+  auto NumArgs = C->getNumArgs();
+  if (NumArgs == 1 ||
+  (NumArgs > 1 && isa(C->getArg(1 {
+Expr *A = C->getArg(0);
+if (A->getSourceRange() == E->getSourceRange())
+  return A;
+  }
+}
+return E;
+  };
+

cor3ntin wrote:

Can you add a comment explaining it's used for std::get/structured binding?

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-16 Thread via cfe-commits

https://github.com/cor3ntin edited 
https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-13 Thread Michael Buch via cfe-commits

Michael137 wrote:

Added tests and updated PR description.

https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-13 Thread Michael Buch via cfe-commits

https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/122265

>From b43ffd0c1bb6e4f1ca5b8458848f574480021b08 Mon Sep 17 00:00:00 2001
From: Michael Buch 
Date: Thu, 9 Jan 2025 10:01:31 +
Subject: [PATCH 1/7] [clang][DebugInfo] Expand detection of structured
 bindings to account for std::get free function

When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups  'int' xvalue
  `-MaterializeTemporaryExpr  'int' xvalue extended by Var 0x11d110cf8 
'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr  'int'
  `-MemberExpr  '' .get 0x11d104390
`-ImplicitCastExpr  'B' xvalue 
  `-DeclRefExpr  'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple>::type &' cinit
`-CallExpr  'const typename tuple_element<0UL, tuple>::type':'const int' lvalue adl
  |-ImplicitCastExpr  'const typename tuple_element<0UL, tuple>::type &(*)(const tuple &) noexcept' 
  | `-DeclRefExpr  'const typename tuple_element<0UL, tuple>::type &(const tuple &) noexcept' lvalue Function 0x1210262d8 
'get' 'const typename tuple_element<0UL, tuple>::type &(const 
tuple &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr  'const std::tuple' lvalue Decomposition 
0x121021518 '' 'const std::tuple &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts our heuristic to account for `CallExpr` nodes.

Fixes https://github.com/llvm/llvm-project/issues/122028
---
 clang/lib/CodeGen/CGDebugInfo.cpp | 28 ++-
 .../debug-info-structured-binding.cpp | 28 ++-
 .../TestStructuredBinding.py  | 11 +++
 .../API/lang/cpp/structured-binding/main.cpp  | 81 +--
 4 files changed, 140 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index d7e5e95b7873a0..f49e759e50ea3d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -85,12 +85,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   if (!Init)
 return false;
 
+  Init = Init->IgnoreUnlessSpelledInSource();
+  if (!Init)
+return false;
+
+  // For tuple-like decompositions, if the `get` function
+  // is not a member of the decomposed type, but instead a
+  // free function (e.g., how std::get is specialized for
+  // std::pair), then the initializer is a `CallExpr` and
+  // we need to dig into the argument before unwrapping it
+  // with IgnoreUnlessSpelledInSource.
+  if (auto const *CE = llvm::dyn_cast(Init)) {
+if (CE->getNumArgs() == 0)
+  return false;
+
+// The first argument will be the type we're decomposing.
+// Technically the getter could have more than 1 argument
+// (e.g., if they're defaulted arguments), but we don't care
+// about them because we just need to check whether the
+// first argument is a DecompositionDecl.
+auto const *Arg = CE->getArg(0);
+if (!Arg)
+  return false;
+
+Init = Arg;
+  }
+
   auto const *RefExpr =
   llvm::dyn_cast_or_null(Init->IgnoreUnlessSpelledInSource());
   if (!RefExpr)
 return false;
 
-  return llvm::dyn_cast_or_null(RefExpr->getDecl());
+  return llvm::isa_and_nonnull(RefExpr->getDecl());
 }
 
 /// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp 
b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index 5fbd54c16382c0..55a84a65015842 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple 
%itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void 
@llvm.dbg.declare"
 
+// CHECK: define noundef i32 @_Z1fv
 // CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], 
!DIExpre

[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-13 Thread via cfe-commits

github-actions[bot] wrote:




:warning: C/C++ code formatter, clang-format found issues in your code. 
:warning:



You can test this locally with the following command:


``bash
git-clang-format --diff 7e01a322f850e86be9eefde8ae5a30e532d22cfa 
66250572fc6a76ea7470028ad3529af8b8aef0b4 --extensions cpp -- 
clang/lib/AST/Expr.cpp clang/test/CodeGenCXX/debug-info-structured-binding.cpp 
clang/unittests/AST/ASTTraverserTest.cpp 
lldb/test/API/lang/cpp/structured-binding/main.cpp
``





View the diff from clang-format here.


``diff
diff --git a/clang/unittests/AST/ASTTraverserTest.cpp 
b/clang/unittests/AST/ASTTraverserTest.cpp
index f3128d4a75..9444d6ba5a 100644
--- a/clang/unittests/AST/ASTTraverserTest.cpp
+++ b/clang/unittests/AST/ASTTraverserTest.cpp
@@ -1604,14 +1604,16 @@ DecompositionDecl ''
 
   {
 auto FN = ast_matchers::match(
-functionDecl(hasName("decompTuple"), 
hasDescendant(callExpr(hasAncestor(varDecl(hasName("a"),
- hasAncestor(bindingDecl().bind("decomp_call"))),
+functionDecl(hasName("decompTuple"),
+ hasDescendant(callExpr(hasAncestor(varDecl(
+hasName("a"),
+hasAncestor(bindingDecl()
+   .bind("decomp_call"))),
 AST2->getASTContext());
 EXPECT_EQ(FN.size(), 1u);
 
-EXPECT_EQ(
-dumpASTString(TK_AsIs, FN[0].getNodeAs("decomp_call")),
-R"cpp(
+EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs("decomp_call")),
+  R"cpp(
 CallExpr
 |-ImplicitCastExpr
 | `-DeclRefExpr 'get'
@@ -1628,8 +1630,11 @@ DeclRefExpr ''
 
   {
 auto FN = ast_matchers::match(
-functionDecl(hasName("decompTuple"), 
hasDescendant(callExpr(hasAncestor(varDecl(hasName("c"),
- 
hasAncestor(bindingDecl().bind("decomp_call_with_default"))),
+functionDecl(hasName("decompTuple"),
+ hasDescendant(callExpr(hasAncestor(varDecl(
+hasName("c"),
+hasAncestor(bindingDecl()
+   .bind("decomp_call_with_default"))),
 AST2->getASTContext());
 EXPECT_EQ(FN.size(), 1u);
 
@@ -1645,9 +1650,10 @@ CallExpr
   `-IntegerLiteral
 )cpp");
 
-EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
-
FN[0].getNodeAs("decomp_call_with_default")),
-  R"cpp(
+EXPECT_EQ(
+dumpASTString(TK_IgnoreUnlessSpelledInSource,
+  FN[0].getNodeAs("decomp_call_with_default")),
+R"cpp(
 DeclRefExpr ''
 )cpp");
   }

``




https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-13 Thread Michael Buch via cfe-commits

https://github.com/Michael137 edited 
https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)

2025-01-13 Thread Michael Buch via cfe-commits

https://github.com/Michael137 edited 
https://github.com/llvm/llvm-project/pull/122265
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits