Skip to content

Commit 042b108

Browse files
author
Gabor Horvath
committed
[cxx-interop] Make ClangImporter support lifetimebound annotated spans
Generate safe Swift Span wrappers using the new SwiftifyImport macro. rdar://139074571
1 parent 4d24b7d commit 042b108

File tree

4 files changed

+73
-16
lines changed

4 files changed

+73
-16
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8778,6 +8778,14 @@ class SwiftifyInfoPrinter {
87788778
out << ")";
87798779
}
87808780

8781+
void printLifetimeboundReturn(int idx, bool borrow) {
8782+
printSeparator();
8783+
out << ".lifetimeDependence(dependsOn: " << (idx + 1);
8784+
out << ", pointer: .return, type: ";
8785+
out << (borrow ? ".borrow" : ".copy");
8786+
out << ")";
8787+
}
8788+
87818789
void printTypeMapping(const llvm::StringMap<std::string> &mapping) {
87828790
printSeparator();
87838791
out << "typeMappings: [";
@@ -8823,23 +8831,44 @@ void ClangImporter::Implementation::importSpanAttributes(FuncDecl *MappedDecl) {
88238831
llvm::raw_svector_ostream out(MacroString);
88248832
llvm::StringMap<std::string> typeMapping;
88258833

8834+
auto registerSwiftifyMacro =
8835+
[&typeMapping, &attachMacro](ParamDecl *swiftParam,
8836+
const clang::ParmVarDecl *param) {
8837+
typeMapping.insert(std::make_pair(
8838+
swiftParam->getInterfaceType()->getString(),
8839+
swiftParam->getInterfaceType()->getDesugaredType()->getString()));
8840+
attachMacro = true;
8841+
};
88268842
SwiftifyInfoPrinter printer(getClangASTContext(), out);
8843+
auto retDecl = ClangDecl->getReturnType()->getAsTagDecl();
8844+
bool returnIsSpan =
8845+
retDecl && retDecl->isInStdNamespace() && retDecl->getName() == "span";
8846+
if (returnIsSpan) {
8847+
typeMapping.insert(
8848+
std::make_pair(MappedDecl->getResultInterfaceType()->getString(),
8849+
MappedDecl->getResultInterfaceType()
8850+
->getDesugaredType()
8851+
->getString()));
8852+
}
88278853
for (auto [index, param] : llvm::enumerate(ClangDecl->parameters())) {
88288854
auto paramTy = param->getType();
88298855
const auto *decl = paramTy->getAsTagDecl();
8830-
if (!decl || !decl->isInStdNamespace())
8831-
continue;
8832-
if (decl->getName() != "span")
8856+
auto swiftParam = MappedDecl->getParameters()->get(index);
8857+
bool isSpan =
8858+
decl && decl->isInStdNamespace() && decl->getName() == "span";
8859+
if (param->hasAttr<clang::LifetimeBoundAttr>() &&
8860+
MappedDecl->getASTContext().LangOpts.hasFeature(
8861+
Feature::LifetimeDependence) &&
8862+
(isSpan || returnIsSpan)) {
8863+
printer.printLifetimeboundReturn(
8864+
index, !isSpan && swiftParam->getInterfaceType()->isEscapable());
8865+
registerSwiftifyMacro(swiftParam, param);
8866+
}
8867+
if (!isSpan)
88338868
continue;
88348869
if (param->hasAttr<clang::NoEscapeAttr>()) {
88358870
printer.printNonEscaping(index);
8836-
clang::PrintingPolicy policy(param->getASTContext().getLangOpts());
8837-
policy.SuppressTagKeyword = true;
8838-
auto param = MappedDecl->getParameters()->get(index);
8839-
typeMapping.insert(std::make_pair(
8840-
paramTy.getAsString(policy),
8841-
param->getInterfaceType()->getDesugaredType()->getString()));
8842-
attachMacro = true;
8871+
registerSwiftifyMacro(swiftParam, param);
88438872
}
88448873
}
88458874
printer.printTypeMapping(typeMapping);

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,15 @@ func replaceBaseType(_ type: TypeSyntax, _ base: TypeSyntax) -> TypeSyntax {
189189
return base
190190
}
191191

192+
// The generated type names for template instantiations sometimes contain
193+
// a `_const` suffix for diambiguation purposes. We need to remove that.
194+
func dropConstSuffix(_ typeName: String) -> String {
195+
if typeName.hasSuffix("_const") {
196+
return String(typeName.dropLast("_const".count))
197+
}
198+
return typeName
199+
}
200+
192201
func getPointerMutability(text: String) -> Mutability? {
193202
switch text {
194203
case "UnsafePointer": return .Immutable
@@ -372,7 +381,8 @@ struct CxxSpanThunkBuilder: ParamPointerBoundsThunkBuilder {
372381
let parsedDesugaredType = TypeSyntax("\(raw: getUnqualifiedStdName(desugaredType)!)")
373382
let genericArg = TypeSyntax(parsedDesugaredType.as(IdentifierTypeSyntax.self)!
374383
.genericArgumentClause!.arguments.first!.argument)!
375-
types[index] = replaceBaseType(param.type, TypeSyntax("Span<\(raw: try getTypeName(genericArg).text)>"))
384+
types[index] = replaceBaseType(param.type,
385+
TypeSyntax("Span<\(raw: dropConstSuffix(try getTypeName(genericArg).text))>"))
376386
return try base.buildFunctionSignature(types, returnType)
377387
}
378388

@@ -407,7 +417,7 @@ struct CxxSpanReturnThunkBuilder: BoundsCheckedThunkBuilder {
407417
let genericArg = TypeSyntax(parsedDesugaredType.as(IdentifierTypeSyntax.self)!
408418
.genericArgumentClause!.arguments.first!.argument)!
409419
let newType = replaceBaseType(signature.returnClause!.type,
410-
TypeSyntax("Span<\(raw: try getTypeName(genericArg).text)>"))
420+
TypeSyntax("Span<\(raw: dropConstSuffix(try getTypeName(genericArg).text))>"))
411421
return try base.buildFunctionSignature(argTypes, newType)
412422
}
413423

test/Interop/Cxx/stdlib/Inputs/std-span.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
#define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SPAN_H
33

44
#include <cstddef>
5-
#include <string>
65
#include <span>
6+
#include <string>
7+
#include <vector>
78

89
using ConstSpanOfInt = std::span<const int>;
910
using SpanOfInt = std::span<int>;
1011
using ConstSpanOfString = std::span<const std::string>;
1112
using SpanOfString = std::span<std::string>;
13+
using VecOfInt = std::vector<int>;
1214

1315
static int iarray[]{1, 2, 3};
1416
static std::string sarray[]{"", "ab", "abc"};
@@ -54,6 +56,15 @@ inline SpanOfInt initSpan(int arr[], size_t size) {
5456

5557
inline struct SpanBox getStructSpanBox() { return {iarray, iarray, sarray, sarray}; }
5658

57-
void funcWithSafeWrapper(SpanOfInt s [[clang::noescape]]) {}
59+
inline void funcWithSafeWrapper(ConstSpanOfInt s [[clang::noescape]]) {}
60+
61+
inline ConstSpanOfInt funcWithSafeWrapper2(ConstSpanOfInt s
62+
[[clang::lifetimebound]]) {
63+
return s;
64+
}
65+
inline ConstSpanOfInt funcWithSafeWrapper3(const VecOfInt &v
66+
[[clang::lifetimebound]]) {
67+
return ConstSpanOfInt(v.data(), v.size());
68+
}
5869

5970
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SPAN_H
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-ide-test -plugin-path %swift-plugin-dir -I %S/Inputs -enable-experimental-feature Span -enable-experimental-feature SafeInteropWrappers -print-module -module-to-print=StdSpan -source-filename=x -enable-experimental-cxx-interop -Xcc -std=c++20 -module-cache-path %t > %t/interface.swift
2+
// RUN: %target-swift-ide-test -plugin-path %swift-plugin-dir -I %S/Inputs -enable-experimental-feature LifetimeDependence -enable-experimental-feature Span -enable-experimental-feature SafeInteropWrappers -print-module -module-to-print=StdSpan -source-filename=x -enable-experimental-cxx-interop -Xcc -std=c++20 -module-cache-path %t > %t/interface.swift
33
// RUN: %FileCheck %s < %t/interface.swift
44

55
// REQUIRES: swift_feature_SafeInteropWrappers
66
// REQUIRES: swift_feature_Span
7+
// REQUIRES: swift_feature_LifetimeDependence
78

89
// FIXME swift-ci linux tests do not support std::span
910
// UNSUPPORTED: OS=linux-gnu
@@ -13,5 +14,11 @@ import StdSpan
1314
#endif
1415
import CxxStdlib
1516

16-
// CHECK: func funcWithSafeWrapper(_ s: SpanOfInt)
17+
// CHECK: func funcWithSafeWrapper(_ s: ConstSpanOfInt)
18+
// CHECK-NEXT: func funcWithSafeWrapper2(_ s: borrowing ConstSpanOfInt) -> ConstSpanOfInt
19+
// CHECK-NEXT: func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> ConstSpanOfInt
1720
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper(_ s: Span<CInt>)
21+
// CHECK-NEXT: @lifetime(s)
22+
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper2(_ s: borrowing Span<CInt>) -> Span<CInt>
23+
// CHECK-NEXT: @lifetime(borrow v)
24+
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> Span<CInt>

0 commit comments

Comments
 (0)