This fixes some problems wrt dependence of captures in lambdas with an explicit object parameter. [temp.dep.expr] states that > An id-expression is type-dependent if [...] its terminal name is > - associated by name lookup with an entity captured by copy > ([expr.prim.lambda.capture]) in a lambda-expression that has > an explicit object parameter whose type is dependent [dcl.fct]. There were several issues with our implementation of this: 1. we were treating by-reference captures as dependent rather than by-value captures; 2. tree transform wasn't checking whether referring to such a by-value capture should make a DRE dependent; 3. when checking whether a DRE refers to such a by-value capture, we were only looking at the immediately enclosing lambda, and not at any parent lambdas; 4. we also forgot to check for implicit by-value captures; 5. lastly, we were attempting to determine whether a lambda has an explicit object parameter by checking the `LambdaScopeInfo`'s `ExplicitObjectParameter`, but it seems that that simply wasn't set (yet) by the time we got to the check. All of these should be fixed now. This fixes #70604, #79754, #84163, #84425, #86054, #86398, and #86399.
185 lines
4.9 KiB
C++
185 lines
4.9 KiB
C++
// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple x86_64-linux -o - | FileCheck %s
|
|
|
|
struct TrivialStruct {
|
|
void explicit_object_function(this TrivialStruct) {}
|
|
};
|
|
void test() {
|
|
TrivialStruct s;
|
|
s.explicit_object_function();
|
|
}
|
|
// CHECK: define {{.*}}test{{.*}}
|
|
// CHECK-NEXT: entry:
|
|
// CHECK: {{.*}} = alloca %struct.TrivialStruct, align 1
|
|
// CHECK: {{.*}} = alloca %struct.TrivialStruct, align 1
|
|
// CHECK: call void {{.*}}explicit_object_function{{.*}}
|
|
// CHECK-NEXT: ret void
|
|
// CHECK-NEXT: }
|
|
|
|
// CHECK: define {{.*}}explicit_object_function{{.*}}
|
|
// CHECK-NEXT: entry:
|
|
// CHECK: {{.*}} = alloca %struct.TrivialStruct, align 1
|
|
// CHECK: ret void
|
|
// CHECK-NEXT: }
|
|
|
|
|
|
void test_lambda() {
|
|
[](this auto This) -> int {
|
|
return This();
|
|
}();
|
|
}
|
|
|
|
//CHECK: define dso_local void @{{.*}}test_lambda{{.*}}() #0 {
|
|
//CHECK: entry:
|
|
//CHECK: %agg.tmp = alloca %class.anon, align 1
|
|
//CHECK: %ref.tmp = alloca %class.anon, align 1
|
|
//CHECK: %call = call noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"()
|
|
//CHECK: ret void
|
|
//CHECK: }
|
|
|
|
//CHECK: define internal noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"() #0 align 2 {
|
|
//CHECK: entry:
|
|
//CHECK: %This = alloca %class.anon, align 1
|
|
//CHECK: %agg.tmp = alloca %class.anon, align 1
|
|
//CHECK: %call = call noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"()
|
|
//CHECK: ret i32 %call
|
|
//CHECK: }
|
|
|
|
void test_lambda_ref() {
|
|
auto l = [i = 42](this auto & This, int j) -> int {
|
|
return This(j);
|
|
};
|
|
l(0);
|
|
}
|
|
|
|
// CHECK: define dso_local void @_Z15test_lambda_refv() #0 {
|
|
// CHECK: entry:
|
|
// CHECK: %[[This_address:.]] = alloca %class.anon{{.*}}, align 4
|
|
// CHECK: %[[i_addr:.*]] = getelementptr inbounds %class.anon{{.*}}, ptr %[[This_address]], i32 0, i32 0
|
|
// CHECK: store i32 42, ptr %[[i_addr]], align 4
|
|
// CHECK: %call = call noundef i32 @"_ZZ15test_lambda_refvENH3$_0clIS_EEiRT_i"{{.*}}
|
|
// CHECK: ret void
|
|
// CHECK: }
|
|
|
|
// CHECK: define internal noundef i32 @"_ZZ15test_lambda_refvENH3$_0clIS_EEiRT_i"{{.*}}
|
|
// CHECK: entry:
|
|
// CHECK: %This.addr = alloca ptr, align 8
|
|
// CHECK: %j.addr = alloca i32, align 4
|
|
// CHECK: store ptr %This, ptr %This.addr, align 8
|
|
// CHECK: store i32 %j, ptr %j.addr, align 4
|
|
// CHECK: %[[this_addr:.*]] = load ptr, ptr %This.addr, align 8
|
|
// CHECK: %[[j_addr:.*]] = load i32, ptr %j.addr, align 4
|
|
// CHECK: %call = call noundef i32 @"_ZZ15test_lambda_refvENH3$_0clIS_EEiRT_i"(ptr noundef nonnull align 4 dereferenceable(4) %[[this_addr]], i32 noundef %[[j_addr]])
|
|
// CHECK: ret i32 %call
|
|
// CHECK: }
|
|
|
|
|
|
struct TestPointer {
|
|
void f(this TestPointer &);
|
|
};
|
|
|
|
void test_pointer() {
|
|
TestPointer t;
|
|
using Fn = void(TestPointer&);
|
|
Fn* fn = &TestPointer::f;
|
|
fn(t);
|
|
}
|
|
//CHECK: define dso_local void @_Z12test_pointerv() #0 {
|
|
//CHECK-NEXT: entry:
|
|
//CHECK-NEXT: %t = alloca %struct.TestPointer, align 1
|
|
//CHECK-NEXT: %fn = alloca ptr, align 8
|
|
//CHECK-NEXT: store ptr @_ZNH11TestPointer1fERS_, ptr %fn, align 8
|
|
//CHECK: %[[fn_ptr:.*]] = load ptr, ptr %fn, align 8
|
|
//CHECK-NEXT: call void %[[fn_ptr]](ptr noundef nonnull align 1 dereferenceable(1) %t)
|
|
//CHECK-NEXT: ret void
|
|
//CHECK-NEXT: }
|
|
|
|
|
|
struct MaterializedTemporary {
|
|
void foo(this MaterializedTemporary&&);
|
|
MaterializedTemporary();
|
|
~MaterializedTemporary();
|
|
};
|
|
|
|
void test_temporary() {
|
|
MaterializedTemporary{}.foo();
|
|
}
|
|
|
|
//CHECK: define dso_local void @_Z14test_temporaryv(){{.*}}
|
|
//CHECK-NEXT: entry:
|
|
//CHECK: %ref.tmp = alloca %struct.MaterializedTemporary, align 1
|
|
//CHECK: call void @_ZN21MaterializedTemporaryC1Ev(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
|
|
//CHECK invoke void @_ZNH21MaterializedTemporary3fooEOS_(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
|
|
|
|
namespace GH86399 {
|
|
volatile int a = 0;
|
|
struct function {
|
|
function& operator=(function const&) {
|
|
a = 1;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
void f() {
|
|
function list;
|
|
|
|
//CHECK-LABEL: define internal void @"_ZZN7GH863991f{{.*}}"(ptr %{{.*}})
|
|
//CHECK: call {{.*}} @_ZN7GH863998functionaSERKS0_
|
|
//CHECK-NEXT: ret void
|
|
[&list](this auto self) {
|
|
list = function{};
|
|
}();
|
|
}
|
|
}
|
|
|
|
namespace GH84163 {
|
|
// Just check that this doesn't crash (we were previously not instantiating
|
|
// everything that needs instantiating in here).
|
|
template <typename> struct S {};
|
|
|
|
void a() {
|
|
int x;
|
|
const auto l = [&x](this auto&) { S<decltype(x)> q; };
|
|
l();
|
|
}
|
|
}
|
|
|
|
namespace GH84425 {
|
|
// As above.
|
|
void do_thing(int x) {
|
|
auto second = [&](this auto const& self, int b) -> int {
|
|
if (x) return x;
|
|
else return self(x);
|
|
};
|
|
|
|
second(1);
|
|
}
|
|
|
|
void do_thing2(int x) {
|
|
auto second = [&](this auto const& self) {
|
|
if (true) return x;
|
|
else return x;
|
|
};
|
|
|
|
second();
|
|
}
|
|
}
|
|
|
|
namespace GH79754 {
|
|
// As above.
|
|
void f() {
|
|
int x;
|
|
[&x](this auto&&) {return x;}();
|
|
}
|
|
}
|
|
|
|
namespace GH70604 {
|
|
auto dothing(int num)
|
|
{
|
|
auto fun = [&num](this auto&& self) -> void {
|
|
auto copy = num;
|
|
};
|
|
|
|
fun();
|
|
}
|
|
}
|