Skip to content

Commit a09d1f0

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 6e84b8f commit a09d1f0

File tree

7 files changed

+113
-13
lines changed

7 files changed

+113
-13
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,8 @@ void importer::getNormalInvocationArguments(
580580
}
581581
}
582582

583-
if (LangOpts.hasFeature(Feature::SafeInteropWrappers))
583+
if (LangOpts.hasFeature(Feature::SafeInteropWrappers) &&
584+
!LangOpts.EnableCXXInterop)
584585
invocationArgStrs.push_back("-fexperimental-bounds-safety-attributes");
585586

586587
// Set C language options.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "clang/AST/Decl.h"
6060
#include "clang/AST/DeclCXX.h"
6161
#include "clang/AST/DeclObjCCommon.h"
62+
#include "clang/AST/PrettyPrinter.h"
6263
#include "clang/AST/Type.h"
6364
#include "clang/Basic/Specifiers.h"
6465
#include "clang/Basic/TargetInfo.h"
@@ -70,10 +71,12 @@
7071
#include "llvm/ADT/SmallString.h"
7172
#include "llvm/ADT/Statistic.h"
7273
#include "llvm/ADT/StringExtras.h"
74+
#include "llvm/ADT/StringMap.h"
7375
#include "llvm/ADT/StringSwitch.h"
7476
#include "llvm/Support/Path.h"
7577

7678
#include <algorithm>
79+
#include <utility>
7780

7881
#define DEBUG_TYPE "Clang module importer"
7982

@@ -129,6 +132,7 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc,
129132
impl.importSwiftAttrAttributes(decl);
130133
if (hasBoundsAnnotation)
131134
impl.importBoundsAttributes(decl);
135+
impl.importSpanAttributes(decl);
132136

133137
return decl;
134138
}
@@ -8684,11 +8688,7 @@ class SwiftifyInfoPrinter {
86848688

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

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

159+
// Remove std. or std.__1. prefix
160+
func getUnqualifiedStdName(_ type: String) -> String? {
161+
if (type.hasPrefix("std.")) {
162+
var ty = type.dropFirst(4)
163+
if (ty.hasPrefix("__1.")) {
164+
ty = ty.dropFirst(4)
165+
}
166+
return String(ty)
167+
}
168+
return nil
169+
}
170+
159171
func getSafePointerName(mut: Mutability, generateSpan: Bool, isRaw: Bool) -> TokenSyntax {
160172
switch (mut, generateSpan, isRaw) {
161173
case (.Immutable, true, true): return "RawSpan"
@@ -299,9 +311,10 @@ struct CxxSpanThunkBuilder: BoundsCheckedThunkBuilder {
299311
"unable to desugar type with name '\(typeName)'", node: node)
300312
}
301313

302-
let parsedDesugaredType = try TypeSyntax("\(raw: desugaredType)")
303-
types[index] = TypeSyntax(IdentifierTypeSyntax(name: "Span",
304-
genericArgumentClause: parsedDesugaredType.as(IdentifierTypeSyntax.self)!.genericArgumentClause))
314+
let parsedDesugaredType = try TypeSyntax("\(raw: getUnqualifiedStdName(desugaredType)!)")
315+
let genericArg = TypeSyntax(parsedDesugaredType.as(IdentifierTypeSyntax.self)!
316+
.genericArgumentClause!.arguments.first!.argument)!
317+
types[index] = TypeSyntax("Span<\(raw: try getTypeName(genericArg).text)>")
305318
return try base.buildFunctionSignature(types, variant)
306319
}
307320

@@ -656,9 +669,11 @@ public struct SwiftifyImportMacro: PeerMacro {
656669
for (idx, param) in signature.parameterClause.parameters.enumerated() {
657670
let typeName = try getTypeName(param.type).text;
658671
if let desugaredType = typeMappings[typeName] {
659-
if desugaredType.starts(with: "span") {
660-
result.append(CxxSpan(pointerIndex: idx + 1, nonescaping: false,
661-
original: param, typeMappings: typeMappings))
672+
if let unqualifiedDesugaredType = getUnqualifiedStdName(desugaredType) {
673+
if unqualifiedDesugaredType.starts(with: "span<") {
674+
result.append(CxxSpan(pointerIndex: idx + 1, nonescaping: false,
675+
original: param, typeMappings: typeMappings))
676+
}
662677
}
663678
}
664679
}

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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
#if !BRIDGING_HEADER
9+
import StdSpan
10+
#endif
11+
import CxxStdlib
12+
13+
// CHECK: func funcWithSafeWrapper(_ s: SpanOfInt)
14+
// 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)