Skip to content

Commit 4846c56

Browse files
author
Gabor Horvath
committed
[cxx-interop] Generate safe overloads for non-escapable spans
A previous PR already added support to the SwiftifyImport macro to generate safe wrappers. This PR makes ClangImporter emit the macro to do the transformation.
1 parent 0ba886d commit 4846c56

File tree

7 files changed

+116
-13
lines changed

7 files changed

+116
-13
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,8 @@ void importer::getNormalInvocationArguments(
566566
}
567567
}
568568

569-
if (LangOpts.hasFeature(Feature::SafeInteropWrappers))
569+
if (LangOpts.hasFeature(Feature::SafeInteropWrappers) &&
570+
!LangOpts.EnableCXXInterop)
570571
invocationArgStrs.push_back("-fexperimental-bounds-safety-attributes");
571572

572573
// Set C language options.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "clang/AST/Decl.h"
6161
#include "clang/AST/DeclCXX.h"
6262
#include "clang/AST/DeclObjCCommon.h"
63+
#include "clang/AST/PrettyPrinter.h"
6364
#include "clang/AST/Type.h"
6465
#include "clang/Basic/Specifiers.h"
6566
#include "clang/Basic/TargetInfo.h"
@@ -71,10 +72,12 @@
7172
#include "llvm/ADT/SmallString.h"
7273
#include "llvm/ADT/Statistic.h"
7374
#include "llvm/ADT/StringExtras.h"
75+
#include "llvm/ADT/StringMap.h"
7476
#include "llvm/ADT/StringSwitch.h"
7577
#include "llvm/Support/Path.h"
7678

7779
#include <algorithm>
80+
#include <utility>
7881

7982
#define DEBUG_TYPE "Clang module importer"
8083

@@ -130,6 +133,7 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc,
130133
impl.importSwiftAttrAttributes(decl);
131134
if (hasBoundsAnnotation)
132135
impl.importBoundsAttributes(decl);
136+
impl.importSpanAttributes(decl);
133137

134138
return decl;
135139
}
@@ -8685,11 +8689,7 @@ class SwiftifyInfoPrinter {
86858689

86868690
void printCountedBy(const clang::CountAttributedType *CAT,
86878691
size_t pointerIndex) {
8688-
if (!firstParam) {
8689-
out << ", ";
8690-
} else {
8691-
firstParam = false;
8692-
}
8692+
printSeparator();
86938693
clang::Expr *countExpr = CAT->getCountExpr();
86948694
bool isSizedBy = CAT->isCountInBytes();
86958695
out << ".";
@@ -8707,9 +8707,76 @@ class SwiftifyInfoPrinter {
87078707
out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr
87088708
out << "\")";
87098709
}
8710+
8711+
void printNonEscaping(int idx) {
8712+
printSeparator();
8713+
out << ".nonescaping(pointer: " << idx << ")";
8714+
}
8715+
8716+
void printTypeMapping(const llvm::StringMap<std::string> &mapping) {
8717+
printSeparator();
8718+
out << "typeMappings: [";
8719+
if (mapping.empty()) {
8720+
out << ":]";
8721+
return;
8722+
}
8723+
llvm::interleaveComma(mapping, out, [&](const auto &entry) {
8724+
out << '"' << entry.getKey() << "\" : \"" << entry.getValue() << '"';
8725+
});
8726+
out << "]";
8727+
}
8728+
8729+
private:
8730+
void printSeparator() {
8731+
if (!firstParam) {
8732+
out << ", ";
8733+
} else {
8734+
firstParam = false;
8735+
}
8736+
}
87108737
};
87118738
} // namespace
87128739

8740+
void ClangImporter::Implementation::importSpanAttributes(FuncDecl *MappedDecl) {
8741+
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
8742+
return;
8743+
auto ClangDecl =
8744+
dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
8745+
if (!ClangDecl)
8746+
return;
8747+
8748+
llvm::SmallString<128> MacroString;
8749+
bool attachMacro = false;
8750+
{
8751+
llvm::raw_svector_ostream out(MacroString);
8752+
llvm::StringMap<std::string> typeMapping;
8753+
8754+
SwiftifyInfoPrinter printer(getClangASTContext(), out);
8755+
for (auto [index, param] : llvm::enumerate(ClangDecl->parameters())) {
8756+
auto paramTy = param->getType();
8757+
const auto *decl = paramTy->getAsTagDecl();
8758+
if (!decl || !decl->isInStdNamespace())
8759+
continue;
8760+
if (decl->getName() != "span")
8761+
continue;
8762+
if (param->hasAttr<clang::NoEscapeAttr>()) {
8763+
printer.printNonEscaping(index + 1);
8764+
clang::PrintingPolicy policy(param->getASTContext().getLangOpts());
8765+
policy.SuppressTagKeyword = true;
8766+
auto param = MappedDecl->getParameters()->get(index);
8767+
typeMapping.insert(std::make_pair(
8768+
paramTy.getAsString(policy),
8769+
param->getInterfaceType()->getDesugaredType()->getString()));
8770+
attachMacro = true;
8771+
}
8772+
}
8773+
printer.printTypeMapping(typeMapping);
8774+
}
8775+
8776+
if (attachMacro)
8777+
importNontrivialAttribute(MappedDecl, MacroString);
8778+
}
8779+
87138780
void ClangImporter::Implementation::importBoundsAttributes(
87148781
FuncDecl *MappedDecl) {
87158782
assert(SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers));

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17461746

17471747
void importSwiftAttrAttributes(Decl *decl);
17481748
void importBoundsAttributes(FuncDecl *MappedDecl);
1749+
void importSpanAttributes(FuncDecl *MappedDecl);
17491750

17501751
/// Find the lookup table that corresponds to the given Clang module.
17511752
///

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,18 @@ func isRawPointerType(text: String) -> Bool {
167167
}
168168
}
169169

170+
// Remove std. or std.__1. prefix
171+
func getUnqualifiedStdName(_ type: String) -> String? {
172+
if (type.hasPrefix("std.")) {
173+
var ty = type.dropFirst(4)
174+
if (ty.hasPrefix("__1.")) {
175+
ty = ty.dropFirst(4)
176+
}
177+
return String(ty)
178+
}
179+
return nil
180+
}
181+
170182
func getSafePointerName(mut: Mutability, generateSpan: Bool, isRaw: Bool) -> TokenSyntax {
171183
switch (mut, generateSpan, isRaw) {
172184
case (.Immutable, true, true): return "RawSpan"
@@ -314,9 +326,10 @@ struct CxxSpanThunkBuilder: BoundsCheckedThunkBuilder {
314326
"unable to desugar type with name '\(typeName)'", node: node)
315327
}
316328

317-
let parsedDesugaredType = TypeSyntax("\(raw: desugaredType)")
318-
types[index] = TypeSyntax(IdentifierTypeSyntax(name: "Span",
319-
genericArgumentClause: parsedDesugaredType.as(IdentifierTypeSyntax.self)!.genericArgumentClause))
329+
let parsedDesugaredType = TypeSyntax("\(raw: getUnqualifiedStdName(desugaredType)!)")
330+
let genericArg = TypeSyntax(parsedDesugaredType.as(IdentifierTypeSyntax.self)!
331+
.genericArgumentClause!.arguments.first!.argument)!
332+
types[index] = TypeSyntax("Span<\(raw: try getTypeName(genericArg).text)>")
320333
return try base.buildFunctionSignature(types, variant)
321334
}
322335

@@ -696,9 +709,11 @@ public struct SwiftifyImportMacro: PeerMacro {
696709
for (idx, param) in signature.parameterClause.parameters.enumerated() {
697710
let typeName = try getTypeName(param.type).text;
698711
if let desugaredType = typeMappings[typeName] {
699-
if desugaredType.starts(with: "span") {
700-
result.append(CxxSpan(pointerIndex: idx + 1, nonescaping: false,
701-
original: param, typeMappings: typeMappings))
712+
if let unqualifiedDesugaredType = getUnqualifiedStdName(desugaredType) {
713+
if unqualifiedDesugaredType.starts(with: "span<") {
714+
result.append(CxxSpan(pointerIndex: idx + 1, nonescaping: false,
715+
original: param, typeMappings: typeMappings))
716+
}
702717
}
703718
}
704719
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,6 @@ inline SpanOfInt initSpan(int arr[], size_t size) {
5454

5555
inline struct SpanBox getStructSpanBox() { return {iarray, iarray, sarray, sarray}; }
5656

57+
void funcWithSafeWrapper(SpanOfInt s [[clang::noescape]]) {}
58+
5759
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SPAN_H
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// 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
3+
// RUN: %FileCheck %s < %t/interface.swift
4+
5+
// REQUIRES: swift_feature_SafeInteropWrappers
6+
// REQUIRES: swift_feature_Span
7+
8+
// FIXME swift-ci linux tests do not support std::span
9+
// UNSUPPORTED: OS=linux-gnu
10+
11+
#if !BRIDGING_HEADER
12+
import StdSpan
13+
#endif
14+
import CxxStdlib
15+
16+
// CHECK: func funcWithSafeWrapper(_ s: SpanOfInt)
17+
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper(_ s: Span<CInt>)

test/Macros/SwiftifyImport/CxxSpan/NoEscapeSpan.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public struct SpanOfInt {
77
init(_ x: Span<CInt>) {}
88
}
99

10-
@_SwiftifyImport(.nonescaping(pointer: 1), typeMappings: ["SpanOfInt" : "span<CInt>"])
10+
@_SwiftifyImport(.nonescaping(pointer: 1), typeMappings: ["SpanOfInt" : "std.span<CInt>"])
1111
func myFunc(_ span: SpanOfInt, _ secondSpan: SpanOfInt) {
1212
}
1313

0 commit comments

Comments
 (0)