[clang] [lldb] [clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (PR #122265)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
@@ -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)
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)
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)
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)
@@ -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)
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)
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)
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)
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)
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)
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
