Skip to content

Commit 00965c2

Browse files
author
Gabor Horvath
committed
[cxx-interop] Teach importer to interpret lifetimebound annotations
The lifetimebound annotations are now imported as lifetime dependencies. This works for basic cases but there are still some parts missing: * Support lifeitmebound annotations on constructors * A way to represent immortal/static lifetimes on the C++ side
1 parent 3f916f5 commit 00965c2

File tree

5 files changed

+154
-15
lines changed

5 files changed

+154
-15
lines changed

include/swift/AST/LifetimeDependence.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,6 @@ class LifetimeDependenceInfo {
229229
static std::optional<ArrayRef<LifetimeDependenceInfo>>
230230
get(AbstractFunctionDecl *decl);
231231

232-
/// Builds LifetimeDependenceInfo from the bitvectors passes as parameters.
233-
static LifetimeDependenceInfo
234-
get(ASTContext &ctx, const SmallBitVector &inheritLifetimeIndices,
235-
const SmallBitVector &scopeLifetimeIndices);
236-
237232
/// Builds LifetimeDependenceInfo from SIL
238233
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
239234
get(FunctionTypeRepr *funcRepr, ArrayRef<SILParameterInfo> params,

lib/ClangImporter/ImportDecl.cpp

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "CFTypeInfo.h"
18-
#include "ImporterImpl.h"
1918
#include "ClangDerivedConformances.h"
19+
#include "ImporterImpl.h"
2020
#include "SwiftDeclSynthesizer.h"
2121
#include "swift/AST/ASTContext.h"
2222
#include "swift/AST/Attr.h"
@@ -53,6 +53,7 @@
5353
#include "swift/Strings.h"
5454
#include "clang/AST/ASTContext.h"
5555
#include "clang/AST/Attr.h"
56+
#include "clang/AST/Decl.h"
5657
#include "clang/AST/DeclCXX.h"
5758
#include "clang/AST/DeclObjCCommon.h"
5859
#include "clang/Basic/Specifiers.h"
@@ -3396,6 +3397,21 @@ namespace {
33963397
return true;
33973398
}
33983399

3400+
static bool
3401+
implicitObjectParamIsLifetimeBound(const clang::FunctionDecl *FD) {
3402+
const clang::TypeSourceInfo *TSI = FD->getTypeSourceInfo();
3403+
if (!TSI)
3404+
return false;
3405+
clang::AttributedTypeLoc ATL;
3406+
for (clang::TypeLoc TL = TSI->getTypeLoc();
3407+
(ATL = TL.getAsAdjusted<clang::AttributedTypeLoc>());
3408+
TL = ATL.getModifiedLoc()) {
3409+
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
3410+
return true;
3411+
}
3412+
return false;
3413+
}
3414+
33993415
Decl *importFunctionDecl(
34003416
const clang::FunctionDecl *decl, ImportedName importedName,
34013417
std::optional<ImportedName> correctSwiftName,
@@ -3702,8 +3718,12 @@ namespace {
37023718
if (!dc->isModuleScopeContext()) {
37033719
if (selfIsInOut)
37043720
func->setSelfAccessKind(SelfAccessKind::Mutating);
3705-
else
3706-
func->setSelfAccessKind(SelfAccessKind::NonMutating);
3721+
else {
3722+
if (implicitObjectParamIsLifetimeBound(decl))
3723+
func->setSelfAccessKind(SelfAccessKind::Borrowing);
3724+
else
3725+
func->setSelfAccessKind(SelfAccessKind::NonMutating);
3726+
}
37073727
if (selfIdx) {
37083728
func->setSelfIndex(selfIdx.value());
37093729
} else {
@@ -3742,13 +3762,70 @@ namespace {
37423762
return result;
37433763
}
37443764

3765+
void addLifetimeDependencies(const clang::FunctionDecl *decl,
3766+
AbstractFunctionDecl *result) {
3767+
if (decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate)
3768+
return;
3769+
3770+
auto swiftParams = result->getParameters();
3771+
bool hasSelf = result->hasImplicitSelfDecl();
3772+
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
3773+
SmallBitVector inheritLifetimeParamIndicesForReturn(swiftParams->size() +
3774+
hasSelf);
3775+
SmallBitVector scopedLifetimeParamIndicesForReturn(swiftParams->size() +
3776+
hasSelf);
3777+
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
3778+
if (param->hasAttr<clang::LifetimeBoundAttr>()) {
3779+
if (swiftParams->get(idx)->getInterfaceType()->isEscapable())
3780+
scopedLifetimeParamIndicesForReturn[idx] = true;
3781+
else
3782+
inheritLifetimeParamIndicesForReturn[idx] = true;
3783+
}
3784+
}
3785+
if (implicitObjectParamIsLifetimeBound(decl)) {
3786+
auto idx = result->getSelfIndex();
3787+
if (result->getImplicitSelfDecl()->getInterfaceType()->isEscapable())
3788+
scopedLifetimeParamIndicesForReturn[idx] = true;
3789+
else
3790+
inheritLifetimeParamIndicesForReturn[idx] = true;
3791+
}
3792+
3793+
if (inheritLifetimeParamIndicesForReturn.any() ||
3794+
scopedLifetimeParamIndicesForReturn.any())
3795+
lifetimeDependencies.push_back(LifetimeDependenceInfo(
3796+
inheritLifetimeParamIndicesForReturn.any()
3797+
? IndexSubset::get(Impl.SwiftContext,
3798+
inheritLifetimeParamIndicesForReturn)
3799+
: nullptr,
3800+
scopedLifetimeParamIndicesForReturn.any()
3801+
? IndexSubset::get(Impl.SwiftContext,
3802+
scopedLifetimeParamIndicesForReturn)
3803+
: nullptr,
3804+
swiftParams->size(),
3805+
/*isImmortal*/ false));
3806+
else if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
3807+
// Assume default constructed view types have no dependencies.
3808+
if (ctordecl->isDefaultConstructor() &&
3809+
importer::hasNonEscapableAttr(ctordecl->getParent()))
3810+
lifetimeDependencies.push_back(
3811+
LifetimeDependenceInfo(nullptr, nullptr, 0, /*isImmortal*/ true));
3812+
}
3813+
if (!lifetimeDependencies.empty()) {
3814+
Impl.SwiftContext.evaluator.cacheOutput(
3815+
LifetimeDependenceInfoRequest{result},
3816+
Impl.SwiftContext.AllocateCopy(lifetimeDependencies));
3817+
}
3818+
}
3819+
37453820
void finishFuncDecl(const clang::FunctionDecl *decl,
37463821
AbstractFunctionDecl *result) {
37473822
// Set availability.
37483823
if (decl->isVariadic()) {
37493824
Impl.markUnavailable(result, "Variadic function is unavailable");
37503825
}
37513826

3827+
addLifetimeDependencies(decl, result);
3828+
37523829
if (decl->hasAttr<clang::ReturnsTwiceAttr>()) {
37533830
// The Clang 'returns_twice' attribute is used for functions like
37543831
// 'vfork' or 'setjmp'. Because these functions may return control flow

lib/ClangImporter/ImportType.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "CFTypeInfo.h"
18-
#include "ClangDiagnosticConsumer.h"
1918
#include "ImporterImpl.h"
2019
#include "SwiftDeclSynthesizer.h"
2120
#include "swift/ABI/MetadataValues.h"
@@ -51,7 +50,6 @@
5150
#include "clang/Sema/Lookup.h"
5251
#include "clang/Sema/Sema.h"
5352
#include "llvm/ADT/SmallString.h"
54-
#include "llvm/ADT/StringExtras.h"
5553
#include "llvm/Support/Compiler.h"
5654

5755
using namespace swift;
@@ -2649,7 +2647,9 @@ static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
26492647
// Foreign references are already references so they don't need to be passed
26502648
// as inout.
26512649
paramInfo->setSpecifier(isInOut ? ParamSpecifier::InOut
2652-
: ParamSpecifier::Default);
2650+
: (param->getAttr<clang::LifetimeBoundAttr>()
2651+
? ParamSpecifier::Borrowing
2652+
: ParamSpecifier::Default));
26532653
paramInfo->setInterfaceType(swiftParamTy);
26542654
impl->recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped);
26552655

test/Interop/Cxx/class/nonescapable.swift renamed to test/Interop/Cxx/class/nonescapable-errors.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@ module Test {
1111
//--- Inputs/nonescapable.h
1212
#include "swift/bridging"
1313

14-
struct SWIFT_NONESCAPABLE A {
15-
int a;
14+
struct SWIFT_NONESCAPABLE View {
15+
View() : member(nullptr) {}
16+
View(const int *p [[clang::lifetimebound]]) : member(p) {}
17+
View(const View&) = default;
18+
private:
19+
const int *member;
1620
};
1721

1822
//--- test.swift
1923

2024
import Test
2125

2226
// CHECK: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
23-
public func test() -> A {
24-
A()
27+
public func noAnnotations() -> View {
28+
View()
2529
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-frontend -typecheck -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs %t/test.swift -enable-experimental-feature NonescapableTypes -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1
4+
5+
//--- Inputs/module.modulemap
6+
module Test {
7+
header "nonescapable.h"
8+
requires cplusplus
9+
}
10+
11+
//--- Inputs/nonescapable.h
12+
#include "swift/bridging"
13+
14+
struct SWIFT_NONESCAPABLE View {
15+
View() : member(nullptr) {}
16+
View(const int *p [[clang::lifetimebound]]) : member(p) {}
17+
View(const View&) = default;
18+
private:
19+
const int *member;
20+
};
21+
22+
struct Owner {
23+
int data;
24+
25+
View handOutView() const [[clang::lifetimebound]] {
26+
return View(&data);
27+
}
28+
};
29+
30+
Owner makeOwner() {
31+
return Owner{42};
32+
}
33+
34+
View getView(const Owner& owner [[clang::lifetimebound]]) {
35+
return View(&owner.data);
36+
}
37+
38+
View getViewFromFirst(const Owner& owner [[clang::lifetimebound]], const Owner& owner2) {
39+
return View(&owner.data);
40+
}
41+
42+
bool coinFlip;
43+
44+
View getViewFromEither(const Owner& owner [[clang::lifetimebound]], const Owner& owner2 [[clang::lifetimebound]]) {
45+
if (coinFlip)
46+
return View(&owner.data);
47+
else
48+
return View(&owner2.data);
49+
}
50+
51+
//--- test.swift
52+
53+
import Test
54+
55+
public func test() {
56+
let o = makeOwner()
57+
let o2 = makeOwner()
58+
let _ = getView(o)
59+
let _ = getViewFromFirst(o, o2)
60+
let _ = getViewFromEither(o, o2)
61+
let _ = o.handOutView()
62+
let defaultView = View()
63+
}

0 commit comments

Comments
 (0)