Skip to content

Commit 745d9b1

Browse files
authored
Merge pull request swiftlang#77829 from swiftlang/gaborh/escapability-annotate-STL
[cxx-interop] Inject escapability for the C++ standard library
2 parents d6b08c3 + 9027edd commit 745d9b1

File tree

3 files changed

+110
-55
lines changed

3 files changed

+110
-55
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
#include <memory>
108108
#include <optional>
109109
#include <string>
110+
#include <utility>
110111

111112
using namespace swift;
112113
using namespace importer;
@@ -5062,6 +5063,60 @@ TinyPtrVector<ValueDecl *> CXXNamespaceMemberLookup::evaluate(
50625063
return result;
50635064
}
50645065

5066+
static const llvm::StringMap<std::vector<int>> STLConditionalEscapableParams{
5067+
{"vector", {0}},
5068+
{"array", {0}},
5069+
{"inplace_vector", {0}},
5070+
{"deque", {0}},
5071+
{"forward_list", {0}},
5072+
{"list", {0}},
5073+
{"set", {0}},
5074+
{"flat_set", {0}},
5075+
{"unordered_set", {0}},
5076+
{"multiset", {0}},
5077+
{"flat_multiset", {0}},
5078+
{"unordered_multiset", {0}},
5079+
{"stack", {0}},
5080+
{"queue", {0}},
5081+
{"priority_queue", {0}},
5082+
{"tuple", {0}},
5083+
{"variant", {0}},
5084+
{"optional", {0}},
5085+
{"pair", {0, 1}},
5086+
{"expected", {0, 1}},
5087+
{"map", {0, 1}},
5088+
{"flat_map", {0, 1}},
5089+
{"unordered_map", {0, 1}},
5090+
{"multimap", {0, 1}},
5091+
{"flat_multimap", {0, 1}},
5092+
{"unordered_multimap", {0, 1}},
5093+
{"span", {0}}, // TODO: remove when span is non-escapable by default
5094+
{"mdspan", {0}}, // TODO: remove when mdspan is non-escapable by default
5095+
};
5096+
5097+
static std::set<StringRef>
5098+
getConditionalEscapableAttrParams(const clang::RecordDecl *decl) {
5099+
std::set<StringRef> result;
5100+
if (!decl->hasAttrs())
5101+
return result;
5102+
for (auto attr : decl->getAttrs()) {
5103+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
5104+
if (swiftAttr->getAttribute().starts_with("escapable_if:")) {
5105+
StringRef params = swiftAttr->getAttribute().drop_front(
5106+
StringRef("escapable_if:").size());
5107+
auto commaPos = params.find(',');
5108+
StringRef nextParam = params.take_front(commaPos);
5109+
while (!nextParam.empty() && commaPos != StringRef::npos) {
5110+
result.insert(nextParam.trim());
5111+
params = params.drop_front(nextParam.size() + 1);
5112+
commaPos = params.find(',');
5113+
nextParam = params.take_front(commaPos);
5114+
}
5115+
}
5116+
}
5117+
return result;
5118+
}
5119+
50655120
CxxEscapability
50665121
ClangTypeEscapability::evaluate(Evaluator &evaluator,
50675122
EscapabilityLookupDescriptor desc) const {
@@ -5083,18 +5138,30 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
50835138
return CxxEscapability::NonEscapable;
50845139
if (hasEscapableAttr(recordDecl))
50855140
return CxxEscapability::Escapable;
5086-
auto conditionalParams =
5087-
importer::getConditionalEscapableAttrParams(recordDecl);
5088-
if (!conditionalParams.empty()) {
5141+
auto injectedStlAnnotation =
5142+
recordDecl->isInStdNamespace()
5143+
? STLConditionalEscapableParams.find(recordDecl->getName())
5144+
: STLConditionalEscapableParams.end();
5145+
bool hasInjectedSTLAnnotation =
5146+
injectedStlAnnotation != STLConditionalEscapableParams.end();
5147+
auto conditionalParams = getConditionalEscapableAttrParams(recordDecl);
5148+
if (!conditionalParams.empty() || hasInjectedSTLAnnotation) {
50895149
auto specDecl = cast<clang::ClassTemplateSpecializationDecl>(recordDecl);
50905150
SmallVector<std::pair<unsigned, StringRef>, 4> argumentsToCheck;
50915151
HeaderLoc loc{recordDecl->getLocation()};
50925152
while (specDecl) {
50935153
auto templateDecl = specDecl->getSpecializedTemplate();
5094-
for (auto [idx, param] :
5095-
llvm::enumerate(*templateDecl->getTemplateParameters())) {
5096-
if (conditionalParams.erase(param->getName()))
5097-
argumentsToCheck.push_back(std::make_pair(idx, param->getName()));
5154+
if (hasInjectedSTLAnnotation) {
5155+
auto params = templateDecl->getTemplateParameters();
5156+
for (auto idx : injectedStlAnnotation->second)
5157+
argumentsToCheck.push_back(
5158+
std::make_pair(idx, params->getParam(idx)->getName()));
5159+
} else {
5160+
for (auto [idx, param] :
5161+
llvm::enumerate(*templateDecl->getTemplateParameters())) {
5162+
if (conditionalParams.erase(param->getName()))
5163+
argumentsToCheck.push_back(std::make_pair(idx, param->getName()));
5164+
}
50985165
}
50995166
auto &argList = specDecl->getTemplateArgs();
51005167
for (auto argToCheck : argumentsToCheck) {
@@ -5118,6 +5185,8 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
51185185
return CxxEscapability::NonEscapable;
51195186
}
51205187
}
5188+
if (hasInjectedSTLAnnotation)
5189+
break;
51215190
clang::DeclContext *dc = specDecl;
51225191
specDecl = nullptr;
51235192
while ((dc = dc->getParent())) {
@@ -7597,29 +7666,6 @@ bool importer::hasEscapableAttr(const clang::RecordDecl *decl) {
75977666
return hasSwiftAttribute(decl, "Escapable");
75987667
}
75997668

7600-
std::set<StringRef>
7601-
importer::getConditionalEscapableAttrParams(const clang::RecordDecl *decl) {
7602-
std::set<StringRef> result;
7603-
if (!decl->hasAttrs())
7604-
return result;
7605-
for (auto attr : decl->getAttrs()) {
7606-
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
7607-
if (swiftAttr->getAttribute().starts_with("escapable_if:")) {
7608-
StringRef params = swiftAttr->getAttribute().drop_front(
7609-
StringRef("escapable_if:").size());
7610-
auto commaPos = params.find(',');
7611-
StringRef nextParam = params.take_front(commaPos);
7612-
while (!nextParam.empty() && commaPos != StringRef::npos) {
7613-
result.insert(nextParam.trim());
7614-
params = params.drop_front(nextParam.size() + 1);
7615-
commaPos = params.find(',');
7616-
nextParam = params.take_front(commaPos);
7617-
}
7618-
}
7619-
}
7620-
return result;
7621-
}
7622-
76237669
/// Recursively checks that there are no pointers in any fields or base classes.
76247670
/// Does not check C++ records with specific API annotations.
76257671
static bool hasPointerInSubobjects(const clang::CXXRecordDecl *decl) {

lib/ClangImporter/ImporterImpl.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,9 +2045,6 @@ bool hasNonEscapableAttr(const clang::RecordDecl *decl);
20452045

20462046
bool hasEscapableAttr(const clang::RecordDecl *decl);
20472047

2048-
std::set<StringRef>
2049-
getConditionalEscapableAttrParams(const clang::RecordDecl *decl);
2050-
20512048
bool isViewType(const clang::CXXRecordDecl *decl);
20522049

20532050
} // end namespace importer

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

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module Test {
1313

1414
//--- Inputs/nonescapable.h
1515
#include "swift/bridging"
16+
#include <vector>
1617

1718
struct SWIFT_NONESCAPABLE View {
1819
View() : member(nullptr) {}
@@ -84,54 +85,65 @@ MyTuple<View> k1();
8485
MyTuple<Owner, View> k2();
8586
MyTuple<Owner, Owner> k3();
8687

88+
using ViewVector = std::vector<View>;
89+
using OwnerVector = std::vector<Owner>;
90+
91+
ViewVector l1();
92+
OwnerVector l2();
93+
8794
//--- test.swift
8895
import Test
96+
import CxxStdlib
8997

9098
// CHECK: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
91-
// CHECK-NO-LIFETIMES: test.swift:5:32: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
99+
// CHECK-NO-LIFETIMES: test.swift:6:32: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
92100
public func noAnnotations() -> View {
93-
// CHECK: nonescapable.h:15:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
101+
// CHECK: nonescapable.h:16:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
94102
f(nil)
95-
// CHECK: nonescapable.h:19:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
103+
// CHECK: nonescapable.h:20:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies
96104
// No duplicate warning for f2:
97-
// CHECK-NOT: nonescapable.h:19
105+
// CHECK-NOT: nonescapable.h:20
98106
f2(nil, nil)
99-
// CHECK: nonescapable.h:23:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated
100-
// CHECK: nonescapable.h:23:6: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
101-
// CHECK-NO-LIFETIMES: nonescapable.h:23:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
107+
// CHECK: nonescapable.h:24:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated
108+
// CHECK: nonescapable.h:24:6: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
109+
// CHECK-NO-LIFETIMES: nonescapable.h:24:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
102110
g(nil)
103111
h1(nil)
104-
// CHECK: nonescapable.h:33:21: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
105-
// CHECK-NO-LIFETIMES: nonescapable.h:33:21: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
106-
h2(nil)
107112
// CHECK: nonescapable.h:34:21: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
108113
// CHECK-NO-LIFETIMES: nonescapable.h:34:21: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
114+
h2(nil)
115+
// CHECK: nonescapable.h:35:21: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
116+
// CHECK-NO-LIFETIMES: nonescapable.h:35:21: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
109117
h3(nil)
110118
i1()
111-
// CHECK: nonescapable.h:38:39: error: template parameter 'Missing' does not exist
112-
// CHECK-NO-LIFETIMES: nonescapable.h:38:39: error: template parameter 'Missing' does not exist
119+
// CHECK: nonescapable.h:39:39: error: template parameter 'Missing' does not exist
120+
// CHECK-NO-LIFETIMES: nonescapable.h:39:39: error: template parameter 'Missing' does not exist
113121
i2()
114-
// CHECK: nonescapable.h:44:33: error: template parameter 'S' expected to be a type parameter
115-
// CHECK-NO-LIFETIMES: nonescapable.h:44:33: error: template parameter 'S' expected to be a type parameter
122+
// CHECK: nonescapable.h:45:33: error: template parameter 'S' expected to be a type parameter
123+
// CHECK-NO-LIFETIMES: nonescapable.h:45:33: error: template parameter 'S' expected to be a type parameter
116124
j1()
117-
// CHECK: nonescapable.h:62:41: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
118-
// CHECK-NO-LIFETIMES: nonescapable.h:62:41: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
119-
j2()
120125
// CHECK: nonescapable.h:63:41: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
121126
// CHECK-NO-LIFETIMES: nonescapable.h:63:41: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
127+
j2()
128+
// CHECK: nonescapable.h:64:41: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
129+
// CHECK-NO-LIFETIMES: nonescapable.h:64:41: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
122130
j3()
123131
k1();
124-
// CHECK: nonescapable.h:69:15: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
125-
// CHECK-NO-LIFETIMES: nonescapable.h:69:15: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
132+
// CHECK: nonescapable.h:70:15: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
133+
// CHECK-NO-LIFETIMES: nonescapable.h:70:15: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
126134
k2();
127-
// CHECK: nonescapable.h:70:22: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
128-
// CHECK-NO-LIFETIMES: nonescapable.h:70:22: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
135+
// CHECK: nonescapable.h:71:22: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
136+
// CHECK-NO-LIFETIMES: nonescapable.h:71:22: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
129137
k3();
138+
l1();
139+
// CHECK: nonescapable.h:77:12: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership
140+
// CHECK-NO-LIFETIMES: nonescapable.h:77:12: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
141+
l2();
130142
// CHECK-NOT: error
131143
// CHECK-NOT: warning
132144
return View()
133-
// CHECK-NO-LIFETIMES: nonescapable.h:4:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
134145
// CHECK-NO-LIFETIMES: nonescapable.h:5:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
146+
// CHECK-NO-LIFETIMES: nonescapable.h:6:5: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'
135147
// CHECK-NO-LIFETIMES-NOT: error
136148
// CHECK-NO-LIFETIMES-NOT: warning
137149
}

0 commit comments

Comments
 (0)