Skip to content

Commit 6b24af6

Browse files
author
Gabor Horvath
committed
[cxx-interop] Always import the lifetime annotations
Importing these annotations were behind the LifetimeDependence experimental flag. However, this feature flag is intended to guard the use of @Lifetime annotations on the Swift side and lifetime inference. The checking of imported lifetime contracts should work even when this flag is off. Removing the flag from the importer caused some fallout. This was mostly due to calling getInterfaceType functions before the import of some Swift declarations were fully done so the code was slightly improved to make decisions only based on the C++ types. There was also a crash when on-member functions imported as methods into Swift. That is worked around in this PR. There is one last feature check that we cannot remove yet, we generate @Lifetime annotations in the SwiftifyImport macro.
1 parent 6eab842 commit 6b24af6

File tree

3 files changed

+38
-49
lines changed

3 files changed

+38
-49
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,12 +2036,9 @@ namespace {
20362036
}
20372037

20382038
void markReturnsUnsafeNonescapable(AbstractFunctionDecl *fd) {
2039-
if (Impl.SwiftContext.LangOpts.hasFeature(Feature::LifetimeDependence)) {
2040-
fd->getAttrs().add(new (Impl.SwiftContext)
2041-
UnsafeNonEscapableResultAttr(/*Implicit=*/true));
2042-
fd->getAttrs().add(new (Impl.SwiftContext)
2043-
UnsafeAttr(/*Implicit=*/true));
2044-
}
2039+
fd->getAttrs().add(new (Impl.SwiftContext)
2040+
UnsafeNonEscapableResultAttr(/*Implicit=*/true));
2041+
fd->getAttrs().add(new (Impl.SwiftContext) UnsafeAttr(/*Implicit=*/true));
20452042
}
20462043

20472044
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
@@ -2417,9 +2414,7 @@ namespace {
24172414
// TODO: builtin "zeroInitializer" does not work with non-escapable
24182415
// types yet. Don't generate an initializer.
24192416
if (hasZeroInitializableStorage && needsEmptyInitializer &&
2420-
(!Impl.SwiftContext.LangOpts.hasFeature(
2421-
Feature::LifetimeDependence) ||
2422-
!isNonEscapable)) {
2417+
!isNonEscapable) {
24232418
// Add default constructor for the struct if compiling in C mode.
24242419
// If we're compiling for C++:
24252420
// 1. If a default constructor is declared, don't synthesize one.
@@ -4045,21 +4040,29 @@ namespace {
40454040
if (decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate)
40464041
return;
40474042

4048-
auto &ASTContext = result->getASTContext();
4049-
if (!ASTContext.LangOpts.hasFeature(Feature::LifetimeDependence))
4043+
// FIXME: support C functions imported as members.
4044+
if (result->getImportAsMemberStatus().isImportAsMember() &&
4045+
!isa<clang::CXXMethodDecl, clang::ObjCMethodDecl>(decl))
40504046
return;
40514047

4048+
auto isEscapable = [this](clang::QualType ty) {
4049+
return evaluateOrDefault(
4050+
Impl.SwiftContext.evaluator,
4051+
ClangTypeEscapability({ty.getTypePtr(), &Impl}),
4052+
CxxEscapability::Unknown) != CxxEscapability::NonEscapable;
4053+
};
4054+
4055+
auto &ASTContext = result->getASTContext();
40524056
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
40534057
LifetimeDependenceInfo immortalLifetime(nullptr, nullptr, 0,
40544058
/*isImmortal*/ true);
4055-
if (const auto *funDecl = dyn_cast<FuncDecl>(result))
4056-
if (hasUnsafeAPIAttr(decl) && !funDecl->getResultInterfaceType()->isEscapable()) {
4057-
lifetimeDependencies.push_back(immortalLifetime);
4058-
Impl.SwiftContext.evaluator.cacheOutput(
4059-
LifetimeDependenceInfoRequest{result},
4060-
Impl.SwiftContext.AllocateCopy(lifetimeDependencies));
4061-
return;
4062-
}
4059+
if (hasUnsafeAPIAttr(decl) && !isEscapable(decl->getReturnType())) {
4060+
lifetimeDependencies.push_back(immortalLifetime);
4061+
Impl.SwiftContext.evaluator.cacheOutput(
4062+
LifetimeDependenceInfoRequest{result},
4063+
Impl.SwiftContext.AllocateCopy(lifetimeDependencies));
4064+
return;
4065+
}
40634066

40644067
auto retType = decl->getReturnType();
40654068
auto warnForEscapableReturnType = [&] {
@@ -4080,23 +4083,24 @@ namespace {
40804083
SmallBitVector scopedLifetimeParamIndicesForReturn(dependencyVecSize);
40814084
SmallBitVector paramHasAnnotation(dependencyVecSize);
40824085
std::map<unsigned, SmallBitVector> inheritedArgDependences;
4083-
auto processLifetimeBound = [&](unsigned idx, Type ty) {
4086+
auto processLifetimeBound = [&](unsigned idx, clang::QualType ty) {
40844087
warnForEscapableReturnType();
40854088
paramHasAnnotation[idx] = true;
4086-
if (ty->isEscapable())
4089+
if (isEscapable(ty))
40874090
scopedLifetimeParamIndicesForReturn[idx] = true;
40884091
else
40894092
inheritLifetimeParamIndicesForReturn[idx] = true;
40904093
};
40914094
auto processLifetimeCaptureBy =
4092-
[&](const clang::LifetimeCaptureByAttr *attr, unsigned idx, Type ty) {
4095+
[&](const clang::LifetimeCaptureByAttr *attr, unsigned idx,
4096+
clang::QualType ty) {
40934097
// FIXME: support scoped lifetimes. This is not straightforward as
40944098
// const T& is imported as taking a value
40954099
// and we assume the address of T would not escape. An
40964100
// annotation in this case contradicts our assumptions. We
40974101
// should diagnose that, and support this for the non-const
40984102
// case.
4099-
if (ty->isEscapable())
4103+
if (isEscapable(ty))
41004104
return;
41014105
for (auto param : attr->params()) {
41024106
// FIXME: Swift assumes no escaping to globals. We should diagnose
@@ -4122,20 +4126,20 @@ namespace {
41224126
};
41234127
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
41244128
if (param->hasAttr<clang::LifetimeBoundAttr>())
4125-
processLifetimeBound(idx, swiftParams->get(idx)->getInterfaceType());
4129+
processLifetimeBound(idx, param->getType());
41264130
if (const auto *attr = param->getAttr<clang::LifetimeCaptureByAttr>())
4127-
processLifetimeCaptureBy(attr, idx,
4128-
swiftParams->get(idx)->getInterfaceType());
4131+
processLifetimeCaptureBy(attr, idx, param->getType());
41294132
}
41304133
if (getImplicitObjectParamAnnotation<clang::LifetimeBoundAttr>(decl))
4131-
processLifetimeBound(result->getSelfIndex(),
4132-
result->getImplicitSelfDecl()->getInterfaceType());
4134+
processLifetimeBound(
4135+
result->getSelfIndex(),
4136+
cast<clang::CXXMethodDecl>(decl)->getThisType()->getPointeeType());
41334137
if (auto attr =
41344138
getImplicitObjectParamAnnotation<clang::LifetimeCaptureByAttr>(
41354139
decl))
41364140
processLifetimeCaptureBy(
41374141
attr, result->getSelfIndex(),
4138-
result->getImplicitSelfDecl()->getInterfaceType());
4142+
cast<clang::CXXMethodDecl>(decl)->getThisType()->getPointeeType());
41394143

41404144
for (auto& [idx, inheritedDepVec]: inheritedArgDependences) {
41414145
lifetimeDependencies.push_back(LifetimeDependenceInfo(inheritedDepVec.any() ? IndexSubset::get(Impl.SwiftContext,
@@ -4180,7 +4184,7 @@ namespace {
41804184
}
41814185

41824186
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
4183-
if (swiftParams->get(idx)->getInterfaceType()->isEscapable())
4187+
if (isEscapable(param->getType()))
41844188
continue;
41854189
if (param->hasAttr<clang::NoEscapeAttr>() || paramHasAnnotation[idx])
41864190
continue;

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,16 @@ import CxxStdlib
103103
// CHECK-NO-LIFETIMES: test.swift:6:32: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
104104
public func noAnnotations() -> View {
105105
// CHECK: nonescapable.h:16:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
106+
// CHECK-NO-LIFETIMES: nonescapable.h:16:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
106107
f(nil)
107108
// CHECK: nonescapable.h:20:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
109+
// CHECK-NO-LIFETIMES: nonescapable.h:20:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
108110
// No duplicate warning for f2:
109111
// CHECK-NOT: nonescapable.h:20
110112
f2(nil, nil)
111113
// CHECK: nonescapable.h:24:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated
112114
// CHECK: nonescapable.h:24:6: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
115+
// CHECK-NO-LIFETIMES: nonescapable.h:24:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated
113116
// CHECK-NO-LIFETIMES: nonescapable.h:24:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
114117
g(nil)
115118
h1(nil)
@@ -144,8 +147,6 @@ public func noAnnotations() -> View {
144147
// CHECK-NO-LIFETIMES: nonescapable.h:77:12: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
145148
l2();
146149
return View()
147-
// CHECK-NO-LIFETIMES: nonescapable.h:5:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
148-
// CHECK-NO-LIFETIMES: nonescapable.h:6:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
149150
}
150151

151152
public func test3(_ x: inout View) {

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

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: rm -rf %t
22
// RUN: split-file %s %t
33
// RUN: %target-swift-frontend -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs -emit-sil %t/test.swift -enable-experimental-feature LifetimeDependence -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
4-
// RUN: not %target-swift-frontend -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs -emit-sil %t/test.swift -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s -check-prefix=CHECK-NO-LIFETIMES
4+
// RUN: %target-swift-frontend -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs -emit-sil %t/test.swift -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
55

66
// REQUIRES: swift_feature_LifetimeDependence
77

@@ -124,22 +124,6 @@ struct SWIFT_NONESCAPABLE AggregateView {
124124
// CHECK: sil [clang CaptureView.captureView] {{.*}} : $@convention(cxx_method) (View, @lifetime(copy 0) @inout CaptureView) -> ()
125125
// CHECK: sil [clang CaptureView.handOut] {{.*}} : $@convention(cxx_method) (@lifetime(copy 1) @inout View, @in_guaranteed CaptureView) -> ()
126126

127-
// CHECK-NO-LIFETIMES: nonescapable.h:35:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
128-
// CHECK-NO-LIFETIMES: nonescapable.h:39:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
129-
// CHECK-NO-LIFETIMES: nonescapable.h:45:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
130-
// CHECK-NO-LIFETIMES: nonescapable.h:52:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
131-
// CHECK-NO-LIFETIMES: nonescapable.h:22:10: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
132-
// CHECK-NO-LIFETIMES: nonescapable.h:26:10: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
133-
// CHECK-NO-LIFETIMES: nonescapable.h:4:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
134-
// CHECK-NO-LIFETIMES: nonescapable.h:5:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
135-
// CHECK-NO-LIFETIMES: nonescapable.h:13:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
136-
// CHECK-NO-LIFETIMES: nonescapable.h:12:27: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
137-
// CHECK-NO-LIFETIMES: nonescapable.h:67:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
138-
// CHECK-NO-LIFETIMES: nonescapable.h:90:13: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
139-
// CHECK-NO-LIFETIMES: nonescapable.h:94:27: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
140-
// CHECK-NO-LIFETIMES: nonescapable.h:94:27: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
141-
// CHECK-NO-LIFETIMES-NOT: error
142-
143127
//--- test.swift
144128

145129
import Test

0 commit comments

Comments
 (0)