Skip to content

Commit 568fa14

Browse files
authored
Merge pull request #77617 from swiftlang/gaborh/support-lifetime-capture-by
[cxx-interop] Support lifetime_capture_by in ClangImporter
2 parents 0fa65be + 8399971 commit 568fa14

File tree

2 files changed

+103
-25
lines changed

2 files changed

+103
-25
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
#include "swift/AST/ConformanceLookup.h"
2626
#include "swift/AST/Decl.h"
2727
#include "swift/AST/DiagnosticsClangImporter.h"
28-
#include "swift/ClangImporter/ClangImporter.h"
2928
#include "swift/AST/ExistentialLayout.h"
3029
#include "swift/AST/Expr.h"
3130
#include "swift/AST/GenericEnvironment.h"
3231
#include "swift/AST/GenericSignature.h"
32+
#include "swift/AST/LifetimeDependence.h"
3333
#include "swift/AST/Module.h"
3434
#include "swift/AST/NameLookup.h"
3535
#include "swift/AST/NameLookupRequests.h"
@@ -49,6 +49,7 @@
4949
#include "swift/Basic/StringExtras.h"
5050
#include "swift/Basic/Version.h"
5151
#include "swift/ClangImporter/CXXMethodBridging.h"
52+
#include "swift/ClangImporter/ClangImporter.h"
5253
#include "swift/ClangImporter/ClangImporterRequests.h"
5354
#include "swift/ClangImporter/ClangModule.h"
5455
#include "swift/Parse/Lexer.h"
@@ -65,6 +66,7 @@
6566
#include "clang/Sema/Lookup.h"
6667

6768
#include "llvm/ADT/STLExtras.h"
69+
#include "llvm/ADT/SmallBitVector.h"
6870
#include "llvm/ADT/SmallString.h"
6971
#include "llvm/ADT/Statistic.h"
7072
#include "llvm/ADT/StringExtras.h"
@@ -3548,19 +3550,20 @@ namespace {
35483550
return true;
35493551
}
35503552

3551-
static bool
3552-
implicitObjectParamIsLifetimeBound(const clang::FunctionDecl *FD) {
3553+
template <typename T>
3554+
static const T *
3555+
getImplicitObjectParamAnnotation(const clang::FunctionDecl *FD) {
35533556
const clang::TypeSourceInfo *TSI = FD->getTypeSourceInfo();
35543557
if (!TSI)
3555-
return false;
3558+
return nullptr;
35563559
clang::AttributedTypeLoc ATL;
35573560
for (clang::TypeLoc TL = TSI->getTypeLoc();
35583561
(ATL = TL.getAsAdjusted<clang::AttributedTypeLoc>());
35593562
TL = ATL.getModifiedLoc()) {
3560-
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
3561-
return true;
3563+
if (auto attr = ATL.getAttrAs<T>())
3564+
return attr;
35623565
}
3563-
return false;
3566+
return nullptr;
35643567
}
35653568

35663569
Decl *importFunctionDecl(
@@ -3872,7 +3875,8 @@ namespace {
38723875
if (selfIsInOut)
38733876
func->setSelfAccessKind(SelfAccessKind::Mutating);
38743877
else {
3875-
if (implicitObjectParamIsLifetimeBound(decl))
3878+
if (getImplicitObjectParamAnnotation<clang::LifetimeBoundAttr>(
3879+
decl))
38763880
func->setSelfAccessKind(SelfAccessKind::Borrowing);
38773881
else
38783882
func->setSelfAccessKind(SelfAccessKind::NonMutating);
@@ -3964,26 +3968,68 @@ namespace {
39643968
auto swiftParams = result->getParameters();
39653969
bool hasSelf =
39663970
result->hasImplicitSelfDecl() && !isa<ConstructorDecl>(result);
3967-
SmallBitVector inheritLifetimeParamIndicesForReturn(swiftParams->size() +
3968-
hasSelf);
3969-
SmallBitVector scopedLifetimeParamIndicesForReturn(swiftParams->size() +
3970-
hasSelf);
3971-
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
3972-
if (param->hasAttr<clang::LifetimeBoundAttr>()) {
3973-
warnForEscapableReturnType();
3974-
if (swiftParams->get(idx)->getInterfaceType()->isEscapable())
3975-
scopedLifetimeParamIndicesForReturn[idx] = true;
3976-
else
3977-
inheritLifetimeParamIndicesForReturn[idx] = true;
3978-
}
3979-
}
3980-
if (implicitObjectParamIsLifetimeBound(decl)) {
3971+
const auto dependencyVecSize = swiftParams->size() + hasSelf;
3972+
SmallBitVector inheritLifetimeParamIndicesForReturn(dependencyVecSize);
3973+
SmallBitVector scopedLifetimeParamIndicesForReturn(dependencyVecSize);
3974+
std::map<unsigned, SmallBitVector> inheritedArgDependences;
3975+
auto processLifetimeBound = [&](unsigned idx, Type ty) {
39813976
warnForEscapableReturnType();
3982-
auto idx = result->getSelfIndex();
3983-
if (result->getImplicitSelfDecl()->getInterfaceType()->isEscapable())
3977+
if (ty->isEscapable())
39843978
scopedLifetimeParamIndicesForReturn[idx] = true;
39853979
else
39863980
inheritLifetimeParamIndicesForReturn[idx] = true;
3981+
};
3982+
auto processLifetimeCaptureBy =
3983+
[&](const clang::LifetimeCaptureByAttr *attr, unsigned idx, Type ty) {
3984+
// FIXME: support scoped lifetimes. This is not straightforward as
3985+
// const T& is imported as taking a value
3986+
// and we assume the address of T would not escape. An
3987+
// annotation in this case contradicts our assumptions. We
3988+
// should diagnose that, and support this for the non-const
3989+
// case.
3990+
if (ty->isEscapable())
3991+
return;
3992+
for (auto param : attr->params()) {
3993+
// FIXME: Swift assumes no escaping to globals. We should diagnose
3994+
// this.
3995+
if (param == clang::LifetimeCaptureByAttr::GLOBAL ||
3996+
param == clang::LifetimeCaptureByAttr::UNKNOWN ||
3997+
param == clang::LifetimeCaptureByAttr::INVALID)
3998+
continue;
3999+
4000+
if (isa<clang::CXXMethodDecl>(decl) &&
4001+
param == clang::LifetimeCaptureByAttr::THIS) {
4002+
auto [it, inserted] = inheritedArgDependences.try_emplace(
4003+
result->getSelfIndex(), SmallBitVector(dependencyVecSize));
4004+
it->second[idx] = true;
4005+
} else {
4006+
auto [it, inserted] = inheritedArgDependences.try_emplace(
4007+
param - isa<clang::CXXMethodDecl>(decl),
4008+
SmallBitVector(dependencyVecSize));
4009+
it->second[idx] = true;
4010+
}
4011+
}
4012+
};
4013+
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
4014+
if (param->hasAttr<clang::LifetimeBoundAttr>())
4015+
processLifetimeBound(idx, swiftParams->get(idx)->getInterfaceType());
4016+
if (const auto *attr = param->getAttr<clang::LifetimeCaptureByAttr>())
4017+
processLifetimeCaptureBy(attr, idx,
4018+
swiftParams->get(idx)->getInterfaceType());
4019+
}
4020+
if (getImplicitObjectParamAnnotation<clang::LifetimeBoundAttr>(decl))
4021+
processLifetimeBound(result->getSelfIndex(),
4022+
result->getImplicitSelfDecl()->getInterfaceType());
4023+
if (auto attr =
4024+
getImplicitObjectParamAnnotation<clang::LifetimeCaptureByAttr>(
4025+
decl))
4026+
processLifetimeCaptureBy(
4027+
attr, result->getSelfIndex(),
4028+
result->getImplicitSelfDecl()->getInterfaceType());
4029+
4030+
for (auto& [idx, inheritedDepVec]: inheritedArgDependences) {
4031+
lifetimeDependencies.push_back(LifetimeDependenceInfo(inheritedDepVec.any() ? IndexSubset::get(Impl.SwiftContext,
4032+
inheritedDepVec): nullptr, nullptr, idx, /*isImmortal=*/false));
39874033
}
39884034

39894035
if (inheritLifetimeParamIndicesForReturn.any() ||

test/Interop/Cxx/class/nonescapable-lifetimebound.swift

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,29 @@ View returnsImmortal() SWIFT_RETURNS_INDEPENDENT_VALUE {
8282
return View();
8383
}
8484

85+
void copyView(View view1 [[clang::lifetime_capture_by(view2)]], View &view2) {
86+
view2 = view1;
87+
}
88+
89+
struct SWIFT_NONESCAPABLE CaptureView {
90+
CaptureView() : view(nullptr) {}
91+
CaptureView(View p [[clang::lifetimebound]]) : view(p) {}
92+
93+
void captureView(View v [[clang::lifetime_capture_by(this)]]) {
94+
view = v;
95+
}
96+
97+
void handOut(View &v) const [[clang::lifetime_capture_by(v)]] {
98+
v = view;
99+
}
100+
101+
View view;
102+
};
103+
104+
CaptureView getCaptureView(const Owner& owner [[clang::lifetimebound]]) {
105+
return CaptureView(View{&owner.data});
106+
}
107+
85108
// CHECK: sil [clang makeOwner] {{.*}}: $@convention(c) () -> Owner
86109
// CHECK: sil [clang getView] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow 0) @autoreleased View
87110
// CHECK: sil [clang getViewFromFirst] {{.*}} : $@convention(c) (@in_guaranteed Owner, @in_guaranteed Owner) -> @lifetime(borrow 0) @autoreleased View
@@ -92,6 +115,10 @@ View returnsImmortal() SWIFT_RETURNS_INDEPENDENT_VALUE {
92115
// CHECK: sil [clang View.init] {{.*}} : $@convention(c) () -> @lifetime(immortal) @out View
93116
// CHECK: sil [clang OtherView.init] {{.*}} : $@convention(c) (@guaranteed View) -> @lifetime(copy 0) @out OtherView
94117
// CHECK: sil [clang returnsImmortal] {{.*}} : $@convention(c) () -> @lifetime(immortal) @autoreleased View
118+
// CHECK: sil [clang copyView] {{.*}} : $@convention(c) (View, @lifetime(copy 0) @inout View) -> ()
119+
// CHECK: sil [clang getCaptureView] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow 0) @autoreleased CaptureView
120+
// CHECK: sil [clang CaptureView.captureView] {{.*}} : $@convention(cxx_method) (View, @lifetime(copy 0) @inout CaptureView) -> ()
121+
// CHECK: sil [clang CaptureView.handOut] {{.*}} : $@convention(cxx_method) (@lifetime(copy 1) @inout View, @in_guaranteed CaptureView) -> ()
95122

96123
// CHECK-NO-LIFETIMES: nonescapable.h:36:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
97124
// CHECK-NO-LIFETIMES: nonescapable.h:40:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
@@ -104,6 +131,7 @@ View returnsImmortal() SWIFT_RETURNS_INDEPENDENT_VALUE {
104131
// CHECK-NO-LIFETIMES: nonescapable.h:13:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
105132
// CHECK-NO-LIFETIMES: nonescapable.h:14:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
106133
// CHECK-NO-LIFETIMES: nonescapable.h:68:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
134+
// CHECK-NO-LIFETIMES: nonescapable.h:91:13: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
107135
// CHECK-NO-LIFETIMES-NOT: error
108136

109137
//--- test.swift
@@ -113,7 +141,7 @@ import Test
113141
public func test() {
114142
let o = makeOwner()
115143
let o2 = makeOwner()
116-
let v1 = getView(o)
144+
var v1 = getView(o)
117145
let v2 = getViewFromFirst(o, o2)
118146
let _ = getViewFromEither(o, o2)
119147
let _ = o.handOutView()
@@ -122,4 +150,8 @@ public func test() {
122150
let defaultView = View()
123151
let _ = OtherView(defaultView)
124152
let _ = returnsImmortal()
153+
copyView(v2, &v1)
154+
var cv = getCaptureView(o)
155+
cv.captureView(v1)
156+
cv.handOut(&v1)
125157
}

0 commit comments

Comments
 (0)