Skip to content

Commit b31b90d

Browse files
authored
Merge pull request #78422 from swiftlang/gaborh/cxx-span-overload-importer
[cxx-interop] Generate safe overloads for non-escapable spans
2 parents eb428be + 4846c56 commit b31b90d

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
}
@@ -8698,11 +8702,7 @@ class SwiftifyInfoPrinter {
86988702

86998703
void printCountedBy(const clang::CountAttributedType *CAT,
87008704
size_t pointerIndex) {
8701-
if (!firstParam) {
8702-
out << ", ";
8703-
} else {
8704-
firstParam = false;
8705-
}
8705+
printSeparator();
87068706
clang::Expr *countExpr = CAT->getCountExpr();
87078707
bool isSizedBy = CAT->isCountInBytes();
87088708
out << ".";
@@ -8720,9 +8720,76 @@ class SwiftifyInfoPrinter {
87208720
out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr
87218721
out << "\")";
87228722
}
8723+
8724+
void printNonEscaping(int idx) {
8725+
printSeparator();
8726+
out << ".nonescaping(pointer: " << idx << ")";
8727+
}
8728+
8729+
void printTypeMapping(const llvm::StringMap<std::string> &mapping) {
8730+
printSeparator();
8731+
out << "typeMappings: [";
8732+
if (mapping.empty()) {
8733+
out << ":]";
8734+
return;
8735+
}
8736+
llvm::interleaveComma(mapping, out, [&](const auto &entry) {
8737+
out << '"' << entry.getKey() << "\" : \"" << entry.getValue() << '"';
8738+
});
8739+
out << "]";
8740+
}
8741+
8742+
private:
8743+
void printSeparator() {
8744+
if (!firstParam) {
8745+
out << ", ";
8746+
} else {
8747+
firstParam = false;
8748+
}
8749+
}
87238750
};
87248751
} // namespace
87258752

8753+
void ClangImporter::Implementation::importSpanAttributes(FuncDecl *MappedDecl) {
8754+
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
8755+
return;
8756+
auto ClangDecl =
8757+
dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
8758+
if (!ClangDecl)
8759+
return;
8760+
8761+
llvm::SmallString<128> MacroString;
8762+
bool attachMacro = false;
8763+
{
8764+
llvm::raw_svector_ostream out(MacroString);
8765+
llvm::StringMap<std::string> typeMapping;
8766+
8767+
SwiftifyInfoPrinter printer(getClangASTContext(), out);
8768+
for (auto [index, param] : llvm::enumerate(ClangDecl->parameters())) {
8769+
auto paramTy = param->getType();
8770+
const auto *decl = paramTy->getAsTagDecl();
8771+
if (!decl || !decl->isInStdNamespace())
8772+
continue;
8773+
if (decl->getName() != "span")
8774+
continue;
8775+
if (param->hasAttr<clang::NoEscapeAttr>()) {
8776+
printer.printNonEscaping(index + 1);
8777+
clang::PrintingPolicy policy(param->getASTContext().getLangOpts());
8778+
policy.SuppressTagKeyword = true;
8779+
auto param = MappedDecl->getParameters()->get(index);
8780+
typeMapping.insert(std::make_pair(
8781+
paramTy.getAsString(policy),
8782+
param->getInterfaceType()->getDesugaredType()->getString()));
8783+
attachMacro = true;
8784+
}
8785+
}
8786+
printer.printTypeMapping(typeMapping);
8787+
}
8788+
8789+
if (attachMacro)
8790+
importNontrivialAttribute(MappedDecl, MacroString);
8791+
}
8792+
87268793
void ClangImporter::Implementation::importBoundsAttributes(
87278794
FuncDecl *MappedDecl) {
87288795
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)