mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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
This commit is contained in:
@@ -235,11 +235,6 @@ public:
|
|||||||
static std::optional<ArrayRef<LifetimeDependenceInfo>>
|
static std::optional<ArrayRef<LifetimeDependenceInfo>>
|
||||||
get(AbstractFunctionDecl *decl);
|
get(AbstractFunctionDecl *decl);
|
||||||
|
|
||||||
/// Builds LifetimeDependenceInfo from the bitvectors passes as parameters.
|
|
||||||
static LifetimeDependenceInfo
|
|
||||||
get(ASTContext &ctx, const SmallBitVector &inheritLifetimeIndices,
|
|
||||||
const SmallBitVector &scopeLifetimeIndices);
|
|
||||||
|
|
||||||
/// Builds LifetimeDependenceInfo from SIL
|
/// Builds LifetimeDependenceInfo from SIL
|
||||||
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
|
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
|
||||||
get(FunctionTypeRepr *funcRepr, ArrayRef<SILParameterInfo> params,
|
get(FunctionTypeRepr *funcRepr, ArrayRef<SILParameterInfo> params,
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "CFTypeInfo.h"
|
#include "CFTypeInfo.h"
|
||||||
#include "ImporterImpl.h"
|
|
||||||
#include "ClangDerivedConformances.h"
|
#include "ClangDerivedConformances.h"
|
||||||
|
#include "ImporterImpl.h"
|
||||||
#include "SwiftDeclSynthesizer.h"
|
#include "SwiftDeclSynthesizer.h"
|
||||||
#include "swift/AST/ASTContext.h"
|
#include "swift/AST/ASTContext.h"
|
||||||
#include "swift/AST/Attr.h"
|
#include "swift/AST/Attr.h"
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
#include "swift/Strings.h"
|
#include "swift/Strings.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/Attr.h"
|
#include "clang/AST/Attr.h"
|
||||||
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclCXX.h"
|
#include "clang/AST/DeclCXX.h"
|
||||||
#include "clang/AST/DeclObjCCommon.h"
|
#include "clang/AST/DeclObjCCommon.h"
|
||||||
#include "clang/Basic/Specifiers.h"
|
#include "clang/Basic/Specifiers.h"
|
||||||
@@ -3474,6 +3475,21 @@ namespace {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
implicitObjectParamIsLifetimeBound(const clang::FunctionDecl *FD) {
|
||||||
|
const clang::TypeSourceInfo *TSI = FD->getTypeSourceInfo();
|
||||||
|
if (!TSI)
|
||||||
|
return false;
|
||||||
|
clang::AttributedTypeLoc ATL;
|
||||||
|
for (clang::TypeLoc TL = TSI->getTypeLoc();
|
||||||
|
(ATL = TL.getAsAdjusted<clang::AttributedTypeLoc>());
|
||||||
|
TL = ATL.getModifiedLoc()) {
|
||||||
|
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Decl *importFunctionDecl(
|
Decl *importFunctionDecl(
|
||||||
const clang::FunctionDecl *decl, ImportedName importedName,
|
const clang::FunctionDecl *decl, ImportedName importedName,
|
||||||
std::optional<ImportedName> correctSwiftName,
|
std::optional<ImportedName> correctSwiftName,
|
||||||
@@ -3780,8 +3796,12 @@ namespace {
|
|||||||
if (!dc->isModuleScopeContext()) {
|
if (!dc->isModuleScopeContext()) {
|
||||||
if (selfIsInOut)
|
if (selfIsInOut)
|
||||||
func->setSelfAccessKind(SelfAccessKind::Mutating);
|
func->setSelfAccessKind(SelfAccessKind::Mutating);
|
||||||
|
else {
|
||||||
|
if (implicitObjectParamIsLifetimeBound(decl))
|
||||||
|
func->setSelfAccessKind(SelfAccessKind::Borrowing);
|
||||||
else
|
else
|
||||||
func->setSelfAccessKind(SelfAccessKind::NonMutating);
|
func->setSelfAccessKind(SelfAccessKind::NonMutating);
|
||||||
|
}
|
||||||
if (selfIdx) {
|
if (selfIdx) {
|
||||||
func->setSelfIndex(selfIdx.value());
|
func->setSelfIndex(selfIdx.value());
|
||||||
} else {
|
} else {
|
||||||
@@ -3820,6 +3840,61 @@ namespace {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addLifetimeDependencies(const clang::FunctionDecl *decl,
|
||||||
|
AbstractFunctionDecl *result) {
|
||||||
|
if (decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto swiftParams = result->getParameters();
|
||||||
|
bool hasSelf = result->hasImplicitSelfDecl();
|
||||||
|
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
|
||||||
|
SmallBitVector inheritLifetimeParamIndicesForReturn(swiftParams->size() +
|
||||||
|
hasSelf);
|
||||||
|
SmallBitVector scopedLifetimeParamIndicesForReturn(swiftParams->size() +
|
||||||
|
hasSelf);
|
||||||
|
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
|
||||||
|
if (param->hasAttr<clang::LifetimeBoundAttr>()) {
|
||||||
|
if (swiftParams->get(idx)->getInterfaceType()->isEscapable())
|
||||||
|
scopedLifetimeParamIndicesForReturn[idx] = true;
|
||||||
|
else
|
||||||
|
inheritLifetimeParamIndicesForReturn[idx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (implicitObjectParamIsLifetimeBound(decl)) {
|
||||||
|
auto idx = result->getSelfIndex();
|
||||||
|
if (result->getImplicitSelfDecl()->getInterfaceType()->isEscapable())
|
||||||
|
scopedLifetimeParamIndicesForReturn[idx] = true;
|
||||||
|
else
|
||||||
|
inheritLifetimeParamIndicesForReturn[idx] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inheritLifetimeParamIndicesForReturn.any() ||
|
||||||
|
scopedLifetimeParamIndicesForReturn.any())
|
||||||
|
lifetimeDependencies.push_back(LifetimeDependenceInfo(
|
||||||
|
inheritLifetimeParamIndicesForReturn.any()
|
||||||
|
? IndexSubset::get(Impl.SwiftContext,
|
||||||
|
inheritLifetimeParamIndicesForReturn)
|
||||||
|
: nullptr,
|
||||||
|
scopedLifetimeParamIndicesForReturn.any()
|
||||||
|
? IndexSubset::get(Impl.SwiftContext,
|
||||||
|
scopedLifetimeParamIndicesForReturn)
|
||||||
|
: nullptr,
|
||||||
|
swiftParams->size(),
|
||||||
|
/*isImmortal*/ false));
|
||||||
|
else if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
|
||||||
|
// Assume default constructed view types have no dependencies.
|
||||||
|
if (ctordecl->isDefaultConstructor() &&
|
||||||
|
importer::hasNonEscapableAttr(ctordecl->getParent()))
|
||||||
|
lifetimeDependencies.push_back(
|
||||||
|
LifetimeDependenceInfo(nullptr, nullptr, 0, /*isImmortal*/ true));
|
||||||
|
}
|
||||||
|
if (!lifetimeDependencies.empty()) {
|
||||||
|
Impl.SwiftContext.evaluator.cacheOutput(
|
||||||
|
LifetimeDependenceInfoRequest{result},
|
||||||
|
Impl.SwiftContext.AllocateCopy(lifetimeDependencies));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void finishFuncDecl(const clang::FunctionDecl *decl,
|
void finishFuncDecl(const clang::FunctionDecl *decl,
|
||||||
AbstractFunctionDecl *result) {
|
AbstractFunctionDecl *result) {
|
||||||
// Set availability.
|
// Set availability.
|
||||||
@@ -3827,6 +3902,8 @@ namespace {
|
|||||||
Impl.markUnavailable(result, "Variadic function is unavailable");
|
Impl.markUnavailable(result, "Variadic function is unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addLifetimeDependencies(decl, result);
|
||||||
|
|
||||||
if (decl->hasAttr<clang::ReturnsTwiceAttr>()) {
|
if (decl->hasAttr<clang::ReturnsTwiceAttr>()) {
|
||||||
// The Clang 'returns_twice' attribute is used for functions like
|
// The Clang 'returns_twice' attribute is used for functions like
|
||||||
// 'vfork' or 'setjmp'. Because these functions may return control flow
|
// 'vfork' or 'setjmp'. Because these functions may return control flow
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "CFTypeInfo.h"
|
#include "CFTypeInfo.h"
|
||||||
#include "ClangDiagnosticConsumer.h"
|
|
||||||
#include "ImporterImpl.h"
|
#include "ImporterImpl.h"
|
||||||
#include "SwiftDeclSynthesizer.h"
|
#include "SwiftDeclSynthesizer.h"
|
||||||
#include "swift/ABI/MetadataValues.h"
|
#include "swift/ABI/MetadataValues.h"
|
||||||
@@ -51,7 +50,6 @@
|
|||||||
#include "clang/Sema/Lookup.h"
|
#include "clang/Sema/Lookup.h"
|
||||||
#include "clang/Sema/Sema.h"
|
#include "clang/Sema/Sema.h"
|
||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
|
||||||
#include "llvm/Support/Compiler.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
|
|
||||||
using namespace swift;
|
using namespace swift;
|
||||||
@@ -2649,7 +2647,9 @@ static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
|
|||||||
// Foreign references are already references so they don't need to be passed
|
// Foreign references are already references so they don't need to be passed
|
||||||
// as inout.
|
// as inout.
|
||||||
paramInfo->setSpecifier(isInOut ? ParamSpecifier::InOut
|
paramInfo->setSpecifier(isInOut ? ParamSpecifier::InOut
|
||||||
: ParamSpecifier::Default);
|
: (param->getAttr<clang::LifetimeBoundAttr>()
|
||||||
|
? ParamSpecifier::Borrowing
|
||||||
|
: ParamSpecifier::Default));
|
||||||
paramInfo->setInterfaceType(swiftParamTy);
|
paramInfo->setInterfaceType(swiftParamTy);
|
||||||
impl->recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped);
|
impl->recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped);
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,12 @@ module Test {
|
|||||||
//--- Inputs/nonescapable.h
|
//--- Inputs/nonescapable.h
|
||||||
#include "swift/bridging"
|
#include "swift/bridging"
|
||||||
|
|
||||||
struct SWIFT_NONESCAPABLE A {
|
struct SWIFT_NONESCAPABLE View {
|
||||||
int a;
|
View() : member(nullptr) {}
|
||||||
|
View(const int *p [[clang::lifetimebound]]) : member(p) {}
|
||||||
|
View(const View&) = default;
|
||||||
|
private:
|
||||||
|
const int *member;
|
||||||
};
|
};
|
||||||
|
|
||||||
//--- test.swift
|
//--- test.swift
|
||||||
@@ -20,6 +24,6 @@ struct SWIFT_NONESCAPABLE A {
|
|||||||
import Test
|
import Test
|
||||||
|
|
||||||
// CHECK: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
|
// CHECK: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
|
||||||
public func test() -> A {
|
public func noAnnotations() -> View {
|
||||||
A()
|
View()
|
||||||
}
|
}
|
||||||
88
test/Interop/Cxx/class/nonescapable-lifetimebound.swift
Normal file
88
test/Interop/Cxx/class/nonescapable-lifetimebound.swift
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// RUN: rm -rf %t
|
||||||
|
// RUN: split-file %s %t
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
|
||||||
|
//--- Inputs/module.modulemap
|
||||||
|
module Test {
|
||||||
|
header "nonescapable.h"
|
||||||
|
requires cplusplus
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- Inputs/nonescapable.h
|
||||||
|
#include "swift/bridging"
|
||||||
|
|
||||||
|
struct SWIFT_NONESCAPABLE View {
|
||||||
|
View() : member(nullptr) {}
|
||||||
|
View(const int *p [[clang::lifetimebound]]) : member(p) {}
|
||||||
|
View(const View&) = default;
|
||||||
|
private:
|
||||||
|
const int *member;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Owner {
|
||||||
|
int data;
|
||||||
|
|
||||||
|
View handOutView() const [[clang::lifetimebound]] {
|
||||||
|
return View(&data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Owner makeOwner() {
|
||||||
|
return Owner{42};
|
||||||
|
}
|
||||||
|
|
||||||
|
View getView(const Owner& owner [[clang::lifetimebound]]) {
|
||||||
|
return View(&owner.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
View getViewFromFirst(const Owner& owner [[clang::lifetimebound]], const Owner& owner2) {
|
||||||
|
return View(&owner.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coinFlip;
|
||||||
|
|
||||||
|
View getViewFromEither(const Owner& owner [[clang::lifetimebound]], const Owner& owner2 [[clang::lifetimebound]]) {
|
||||||
|
if (coinFlip)
|
||||||
|
return View(&owner.data);
|
||||||
|
else
|
||||||
|
return View(&owner2.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
View getViewFromEither(View view1 [[clang::lifetimebound]], View view2 [[clang::lifetimebound]]) {
|
||||||
|
if (coinFlip)
|
||||||
|
return view1;
|
||||||
|
else
|
||||||
|
return view2;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SWIFT_NONESCAPABLE TestAnnotationTranslation {
|
||||||
|
TestAnnotationTranslation() : member(nullptr) {}
|
||||||
|
TestAnnotationTranslation(const int *p [[clang::lifetimebound]]) : member(p) {}
|
||||||
|
TestAnnotationTranslation(const TestAnnotationTranslation& [[clang::lifetimebound]]) = default;
|
||||||
|
private:
|
||||||
|
const int *member;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CHECK: sil [clang makeOwner] {{.*}}: $@convention(c) () -> Owner
|
||||||
|
// CHECK: sil [clang getView] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> _scope(0) @autoreleased View
|
||||||
|
// CHECK: sil [clang getViewFromFirst] {{.*}} : $@convention(c) (@in_guaranteed Owner, @in_guaranteed Owner) -> _scope(0) @autoreleased View
|
||||||
|
// CHECK: sil [clang getViewFromEither] {{.*}} : $@convention(c) (@in_guaranteed Owner, @in_guaranteed Owner) -> _scope(0, 1) @autoreleased View
|
||||||
|
// CHECK: sil [clang Owner.handOutView] {{.*}} : $@convention(cxx_method) (@in_guaranteed Owner) -> _scope(0) @autoreleased View
|
||||||
|
// CHECK: sil [clang getViewFromEither] {{.*}} : $@convention(c) (@guaranteed View, @guaranteed View) -> _inherit(0, 1) @autoreleased View
|
||||||
|
// CHECK: sil [clang View.init] {{.*}} : $@convention(c) () -> @out View
|
||||||
|
|
||||||
|
//--- test.swift
|
||||||
|
|
||||||
|
import Test
|
||||||
|
|
||||||
|
public func test() {
|
||||||
|
let o = makeOwner()
|
||||||
|
let o2 = makeOwner()
|
||||||
|
let v1 = getView(o)
|
||||||
|
let v2 = getViewFromFirst(o, o2)
|
||||||
|
let _ = getViewFromEither(o, o2)
|
||||||
|
let _ = o.handOutView()
|
||||||
|
let _ = getViewFromEither(v1, v2)
|
||||||
|
let defaultView = View()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user