Skip to content

Commit c76f894

Browse files
authored
Merge pull request #77073 from swiftlang/gaborh/lifetimebound-add-diagnostics
[cxx-interop] Diagnose misuses of escapability and lifetimebound
2 parents 763666e + e86cf18 commit c76f894

File tree

3 files changed

+66
-2
lines changed

3 files changed

+66
-2
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,5 +289,8 @@ NOTE(forward_declared_protocol_clashes_with_imported_objc_Swift_protocol, none,
289289
"its name conflicts with a %1 in module %2",
290290
(const clang::NamedDecl*, StringRef, StringRef))
291291

292+
WARNING(return_escapable_with_lifetimebound, none, "the returned type '%0' is annotated as escapable; it cannot have lifetime dependencies", (StringRef))
293+
WARNING(return_nonescapable_without_lifetimebound, none, "the returned type '%0' is annotated as non-escapable; its lifetime dependencies must be annotated", (StringRef))
294+
292295
#define UNDEFINE_DIAGNOSTIC_MACROS
293296
#include "DefineDiagnosticMacros.h"

lib/ClangImporter/ImportDecl.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "clang/AST/Decl.h"
5858
#include "clang/AST/DeclCXX.h"
5959
#include "clang/AST/DeclObjCCommon.h"
60+
#include "clang/AST/Type.h"
6061
#include "clang/Basic/Specifiers.h"
6162
#include "clang/Basic/TargetInfo.h"
6263
#include "clang/Lex/Preprocessor.h"
@@ -3852,11 +3853,36 @@ namespace {
38523853
return result;
38533854
}
38543855

3856+
static bool isNonEscapableAnnotatedType(const clang::Type *t) {
3857+
if (const auto *rd = t->getAsRecordDecl()) {
3858+
return hasNonEscapableAttr(rd);
3859+
}
3860+
return false;
3861+
}
3862+
3863+
static bool isEscapableAnnotatedType(const clang::Type *t) {
3864+
if (const auto *rd = t->getAsRecordDecl()) {
3865+
return hasEscapableAttr(rd);
3866+
}
3867+
return false;
3868+
}
3869+
38553870
void addLifetimeDependencies(const clang::FunctionDecl *decl,
38563871
AbstractFunctionDecl *result) {
38573872
if (decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate)
38583873
return;
38593874

3875+
auto retType = decl->getReturnType();
3876+
auto warnForEscapableReturnType = [&] {
3877+
if (isEscapableAnnotatedType(retType.getTypePtr())) {
3878+
Impl.addImportDiagnostic(
3879+
decl,
3880+
Diagnostic(diag::return_escapable_with_lifetimebound,
3881+
Impl.SwiftContext.AllocateCopy(retType.getAsString())),
3882+
decl->getLocation());
3883+
}
3884+
};
3885+
38603886
auto swiftParams = result->getParameters();
38613887
bool hasSelf = result->hasImplicitSelfDecl() && !isa<ConstructorDecl>(result);
38623888
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
@@ -3866,13 +3892,15 @@ namespace {
38663892
hasSelf);
38673893
for (auto [idx, param] : llvm::enumerate(decl->parameters())) {
38683894
if (param->hasAttr<clang::LifetimeBoundAttr>()) {
3895+
warnForEscapableReturnType();
38693896
if (swiftParams->get(idx)->getInterfaceType()->isEscapable())
38703897
scopedLifetimeParamIndicesForReturn[idx] = true;
38713898
else
38723899
inheritLifetimeParamIndicesForReturn[idx] = true;
38733900
}
38743901
}
38753902
if (implicitObjectParamIsLifetimeBound(decl)) {
3903+
warnForEscapableReturnType();
38763904
auto idx = result->getSelfIndex();
38773905
if (result->getImplicitSelfDecl()->getInterfaceType()->isEscapable())
38783906
scopedLifetimeParamIndicesForReturn[idx] = true;
@@ -3900,11 +3928,20 @@ namespace {
39003928
lifetimeDependencies.push_back(
39013929
LifetimeDependenceInfo(nullptr, nullptr, 0, /*isImmortal*/ true));
39023930
}
3903-
if (!lifetimeDependencies.empty()) {
3931+
if (lifetimeDependencies.empty()) {
3932+
if (isNonEscapableAnnotatedType(retType.getTypePtr())) {
3933+
Impl.addImportDiagnostic(
3934+
decl,
3935+
Diagnostic(diag::return_nonescapable_without_lifetimebound,
3936+
Impl.SwiftContext.AllocateCopy(retType.getAsString())),
3937+
decl->getLocation());
3938+
}
3939+
} else {
39043940
Impl.SwiftContext.evaluator.cacheOutput(
39053941
LifetimeDependenceInfoRequest{result},
39063942
Impl.SwiftContext.AllocateCopy(lifetimeDependencies));
39073943
}
3944+
Impl.diagnoseTargetDirectly(decl);
39083945
}
39093946

39103947
void finishFuncDecl(const clang::FunctionDecl *decl,

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,35 @@ private:
1919
const int *member;
2020
};
2121

22+
struct SWIFT_ESCAPABLE Owner {
23+
int data;
24+
};
25+
26+
Owner f(int* x [[clang::lifetimebound]]) {
27+
return Owner{0};
28+
}
29+
30+
Owner f2(int* x [[clang::lifetimebound]], int* y [[clang::lifetimebound]]) {
31+
return Owner{0};
32+
}
33+
34+
View g(int* x) {
35+
return View(x);
36+
}
37+
2238
//--- test.swift
2339

2440
import Test
2541

2642
// CHECK: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
2743
public func noAnnotations() -> View {
28-
View()
44+
// CHECK: nonescapable.h:15:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
45+
f(nil)
46+
// CHECK: nonescapable.h:19:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
47+
// No duplicate warning for f2:
48+
// CHECK-NOT: nonescapable.h:19
49+
f2(nil, nil)
50+
// CHECK: nonescapable.h:23:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated
51+
g(nil)
52+
return View()
2953
}

0 commit comments

Comments
 (0)