Skip to content

Commit ffa1014

Browse files
authored
Merge pull request #76311 from swiftlang/gaborh/import-lifetimebound
[cxx-interop] Teach importer to interpret lifetimebound annotations
2 parents ad503af + 16e012b commit ffa1014

File tree

5 files changed

+179
-15
lines changed

5 files changed

+179
-15
lines changed

include/swift/AST/LifetimeDependence.h

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

238-
/// Builds LifetimeDependenceInfo from the bitvectors passes as parameters.
239-
static LifetimeDependenceInfo
240-
get(ASTContext &ctx, const SmallBitVector &inheritLifetimeIndices,
241-
const SmallBitVector &scopeLifetimeIndices);
242-
243238
/// Builds LifetimeDependenceInfo from SIL
244239
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
245240
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"
@@ -3474,6 +3475,21 @@ namespace {
34743475
return true;
34753476
}
34763477

3478+
static bool
3479+
implicitObjectParamIsLifetimeBound(const clang::FunctionDecl *FD) {
3480+
const clang::TypeSourceInfo *TSI = FD->getTypeSourceInfo();
3481+
if (!TSI)
3482+
return false;
3483+
clang::AttributedTypeLoc ATL;
3484+
for (clang::TypeLoc TL = TSI->getTypeLoc();
3485+
(ATL = TL.getAsAdjusted<clang::AttributedTypeLoc>());
3486+
TL = ATL.getModifiedLoc()) {
3487+
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
3488+
return true;
3489+
}
3490+
return false;
3491+
}
3492+
34773493
Decl *importFunctionDecl(
34783494
const clang::FunctionDecl *decl, ImportedName importedName,
34793495
std::optional<ImportedName> correctSwiftName,
@@ -3780,8 +3796,12 @@ namespace {
37803796
if (!dc->isModuleScopeContext()) {
37813797
if (selfIsInOut)
37823798
func->setSelfAccessKind(SelfAccessKind::Mutating);
3783-
else
3784-
func->setSelfAccessKind(SelfAccessKind::NonMutating);
3799+
else {
3800+
if (implicitObjectParamIsLifetimeBound(decl))
3801+
func->setSelfAccessKind(SelfAccessKind::Borrowing);
3802+
else
3803+
func->setSelfAccessKind(SelfAccessKind::NonMutating);
3804+
}
37853805
if (selfIdx) {
37863806
func->setSelfIndex(selfIdx.value());
37873807
} else {
@@ -3820,13 +3840,70 @@ namespace {
38203840
return result;
38213841
}
38223842

3843+
void addLifetimeDependencies(const clang::FunctionDecl *decl,
3844+
AbstractFunctionDecl *result) {
3845+
if (decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate)
3846+
return;
3847+
3848+
auto swiftParams = result->getParameters();
3849+
bool hasSelf = result->hasImplicitSelfDecl();
3850+
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
3851+
SmallBitVector inheritLifetimeParamIndicesForReturn(swiftParams->size() +
3852+
hasSelf);
3853+
SmallBitVector scopedLifetimeParamIndicesForReturn(swiftParams->size() +
3854+
hasSelf);
3855+
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
3856+
if (param->hasAttr<clang::LifetimeBoundAttr>()) {
3857+
if (swiftParams->get(idx)->getInterfaceType()->isEscapable())
3858+
scopedLifetimeParamIndicesForReturn[idx] = true;
3859+
else
3860+
inheritLifetimeParamIndicesForReturn[idx] = true;
3861+
}
3862+
}
3863+
if (implicitObjectParamIsLifetimeBound(decl)) {
3864+
auto idx = result->getSelfIndex();
3865+
if (result->getImplicitSelfDecl()->getInterfaceType()->isEscapable())
3866+
scopedLifetimeParamIndicesForReturn[idx] = true;
3867+
else
3868+
inheritLifetimeParamIndicesForReturn[idx] = true;
3869+
}
3870+
3871+
if (inheritLifetimeParamIndicesForReturn.any() ||
3872+
scopedLifetimeParamIndicesForReturn.any())
3873+
lifetimeDependencies.push_back(LifetimeDependenceInfo(
3874+
inheritLifetimeParamIndicesForReturn.any()
3875+
? IndexSubset::get(Impl.SwiftContext,
3876+
inheritLifetimeParamIndicesForReturn)
3877+
: nullptr,
3878+
scopedLifetimeParamIndicesForReturn.any()
3879+
? IndexSubset::get(Impl.SwiftContext,
3880+
scopedLifetimeParamIndicesForReturn)
3881+
: nullptr,
3882+
swiftParams->size(),
3883+
/*isImmortal*/ false));
3884+
else if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
3885+
// Assume default constructed view types have no dependencies.
3886+
if (ctordecl->isDefaultConstructor() &&
3887+
importer::hasNonEscapableAttr(ctordecl->getParent()))
3888+
lifetimeDependencies.push_back(
3889+
LifetimeDependenceInfo(nullptr, nullptr, 0, /*isImmortal*/ true));
3890+
}
3891+
if (!lifetimeDependencies.empty()) {
3892+
Impl.SwiftContext.evaluator.cacheOutput(
3893+
LifetimeDependenceInfoRequest{result},
3894+
Impl.SwiftContext.AllocateCopy(lifetimeDependencies));
3895+
}
3896+
}
3897+
38233898
void finishFuncDecl(const clang::FunctionDecl *decl,
38243899
AbstractFunctionDecl *result) {
38253900
// Set availability.
38263901
if (decl->isVariadic()) {
38273902
Impl.markUnavailable(result, "Variadic function is unavailable");
38283903
}
38293904

3905+
addLifetimeDependencies(decl, result);
3906+
38303907
if (decl->hasAttr<clang::ReturnsTwiceAttr>()) {
38313908
// The Clang 'returns_twice' attribute is used for functions like
38323909
// '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: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
// RUN: %target-swift-frontend -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs -emit-sil %t/test.swift -enable-experimental-feature NonescapableTypes -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
5+
6+
//--- Inputs/module.modulemap
7+
module Test {
8+
header "nonescapable.h"
9+
requires cplusplus
10+
}
11+
12+
//--- Inputs/nonescapable.h
13+
#include "swift/bridging"
14+
15+
struct SWIFT_NONESCAPABLE View {
16+
View() : member(nullptr) {}
17+
View(const int *p [[clang::lifetimebound]]) : member(p) {}
18+
View(const View&) = default;
19+
private:
20+
const int *member;
21+
};
22+
23+
struct Owner {
24+
int data;
25+
26+
View handOutView() const [[clang::lifetimebound]] {
27+
return View(&data);
28+
}
29+
};
30+
31+
Owner makeOwner() {
32+
return Owner{42};
33+
}
34+
35+
View getView(const Owner& owner [[clang::lifetimebound]]) {
36+
return View(&owner.data);
37+
}
38+
39+
View getViewFromFirst(const Owner& owner [[clang::lifetimebound]], const Owner& owner2) {
40+
return View(&owner.data);
41+
}
42+
43+
bool coinFlip;
44+
45+
View getViewFromEither(const Owner& owner [[clang::lifetimebound]], const Owner& owner2 [[clang::lifetimebound]]) {
46+
if (coinFlip)
47+
return View(&owner.data);
48+
else
49+
return View(&owner2.data);
50+
}
51+
52+
View getViewFromEither(View view1 [[clang::lifetimebound]], View view2 [[clang::lifetimebound]]) {
53+
if (coinFlip)
54+
return view1;
55+
else
56+
return view2;
57+
}
58+
59+
struct SWIFT_NONESCAPABLE TestAnnotationTranslation {
60+
TestAnnotationTranslation() : member(nullptr) {}
61+
TestAnnotationTranslation(const int *p [[clang::lifetimebound]]) : member(p) {}
62+
TestAnnotationTranslation(const TestAnnotationTranslation& [[clang::lifetimebound]]) = default;
63+
private:
64+
const int *member;
65+
};
66+
67+
// CHECK: sil [clang makeOwner] {{.*}}: $@convention(c) () -> Owner
68+
// CHECK: sil [clang getView] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> _scope(0) @autoreleased View
69+
// CHECK: sil [clang getViewFromFirst] {{.*}} : $@convention(c) (@in_guaranteed Owner, @in_guaranteed Owner) -> _scope(0) @autoreleased View
70+
// CHECK: sil [clang getViewFromEither] {{.*}} : $@convention(c) (@in_guaranteed Owner, @in_guaranteed Owner) -> _scope(0, 1) @autoreleased View
71+
// CHECK: sil [clang Owner.handOutView] {{.*}} : $@convention(cxx_method) (@in_guaranteed Owner) -> _scope(0) @autoreleased View
72+
// CHECK: sil [clang getViewFromEither] {{.*}} : $@convention(c) (@guaranteed View, @guaranteed View) -> _inherit(0, 1) @autoreleased View
73+
// CHECK: sil [clang View.init] {{.*}} : $@convention(c) () -> @out View
74+
75+
//--- test.swift
76+
77+
import Test
78+
79+
public func test() {
80+
let o = makeOwner()
81+
let o2 = makeOwner()
82+
let v1 = getView(o)
83+
let v2 = getViewFromFirst(o, o2)
84+
let _ = getViewFromEither(o, o2)
85+
let _ = o.handOutView()
86+
let _ = getViewFromEither(v1, v2)
87+
let defaultView = View()
88+
}

0 commit comments

Comments
 (0)