Skip to content

Commit 628fc8a

Browse files
committed
[Swiftify] Add support for free functions imported as instance methods
This adds support for attaching safe interop wrappers to functions explicitly imported as instance methods using swift_name. rdar://156288883
1 parent c82e85d commit 628fc8a

File tree

4 files changed

+200
-10
lines changed

4 files changed

+200
-10
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ GROUPED_WARNING(clang_ignored_sendable_attr, ClangDeclarationImport, none,
105105
"cannot be added to it",
106106
(Type))
107107

108+
GROUPED_WARNING(warn_clang_ignored_bounds_on_self, ClangDeclarationImport, none,
109+
"bounds attribute '%0' ignored on parameter mapped to 'self'",
110+
(StringRef))
111+
NOTE(note_swift_name_instance_method, none,
112+
"swift_name maps free function to instance method here", ())
113+
108114
WARNING(implicit_bridging_header_imported_from_module,none,
109115
"implicit import of bridging header '%0' via module %1 "
110116
"is deprecated and will be removed in a later version of Swift",

lib/ClangImporter/ImportDecl.cpp

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9445,6 +9445,22 @@ void ClangImporter::Implementation::addOptionSetTypealiases(
94459445

94469446
#define SIW_DBG(x) DEBUG_WITH_TYPE("safe-interop-wrappers", llvm::dbgs() << x)
94479447

9448+
// until CountAttributedType::getAttributeName lands in our LLVM branch
9449+
static StringRef getAttributeName(const clang::CountAttributedType *CAT) {
9450+
switch (CAT->getKind()) {
9451+
case clang::CountAttributedType::CountedBy:
9452+
return "__counted_by";
9453+
case clang::CountAttributedType::CountedByOrNull:
9454+
return "__counted_by_or_null";
9455+
case clang::CountAttributedType::SizedBy:
9456+
return "__sized_by";
9457+
case clang::CountAttributedType::SizedByOrNull:
9458+
return "__sized_by_or_null";
9459+
case clang::CountAttributedType::EndedBy:
9460+
llvm_unreachable("CountAttributedType cannot be ended_by");
9461+
}
9462+
}
9463+
94489464
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
94499465
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
94509466
return;
@@ -9460,9 +9476,6 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
94609476
ClangDecl->getAccess() == clang::AS_private)
94619477
return;
94629478

9463-
if (ClangDecl->getNumParams() != MappedDecl->getParameters()->size())
9464-
return;
9465-
94669479
MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(getKnownSingleDecl(SwiftContext, "_SwiftifyImport"));
94679480
if (!SwiftifyImportDecl)
94689481
return;
@@ -9519,14 +9532,42 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
95199532
true);
95209533
returnHasLifetimeInfo = true;
95219534
}
9535+
9536+
bool isClangInstanceMethod =
9537+
isa<clang::CXXMethodDecl>(ClangDecl) &&
9538+
!isa<clang::CXXConstructorDecl>(ClangDecl) &&
9539+
cast<clang::CXXMethodDecl>(ClangDecl)->isInstance();
9540+
ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) ==
9541+
(ClangDecl->getNumParams() == MappedDecl->getParameters()->size()));
9542+
9543+
size_t selfParamIndex = MappedDecl->isImportAsInstanceMember()
9544+
? MappedDecl->getSelfIndex()
9545+
: ClangDecl->getNumParams();
95229546
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
95239547
auto clangParamTy = clangParam->getType();
9524-
auto swiftParam = MappedDecl->getParameters()->get(index);
9548+
int mappedIndex = index < selfParamIndex ? index :
9549+
index > selfParamIndex ? index - 1 :
9550+
SwiftifyInfoPrinter::SELF_PARAM_INDEX;
9551+
ParamDecl *swiftParam = nullptr;
9552+
if (mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
9553+
swiftParam = MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/true);
9554+
} else {
9555+
swiftParam = MappedDecl->getParameters()->get(mappedIndex);
9556+
}
9557+
ASSERT(swiftParam);
95259558
Type swiftParamTy = swiftParam->getInterfaceType();
95269559
bool paramHasBoundsInfo = false;
95279560
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
9528-
if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
9529-
printer.printCountedBy(CAT, index);
9561+
if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
9562+
diagnose(HeaderLoc(clangParam->getLocation()),
9563+
diag::warn_clang_ignored_bounds_on_self, getAttributeName(CAT));
9564+
auto swiftName = ClangDecl->getAttr<clang::SwiftNameAttr>();
9565+
ASSERT(swiftName &&
9566+
"free function mapped to instance method without swift_name??");
9567+
diagnose(HeaderLoc(swiftName->getLocation()),
9568+
diag::note_swift_name_instance_method);
9569+
} else if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
9570+
printer.printCountedBy(CAT, mappedIndex);
95309571
SIW_DBG(" Found bounds info '" << clangParamTy
95319572
<< "' on parameter '" << *clangParam << "'\n");
95329573
attachMacro = paramHasBoundsInfo = true;
@@ -9538,16 +9579,18 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
95389579
bool paramHasLifetimeInfo = false;
95399580
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
95409581
SIW_DBG(" Found noescape attribute on parameter '" << *clangParam << "'\n");
9541-
printer.printNonEscaping(index);
9582+
printer.printNonEscaping(mappedIndex);
95429583
paramHasLifetimeInfo = true;
95439584
}
95449585
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
95459586
SIW_DBG(" Found lifetimebound attribute on parameter '"
95469587
<< *clangParam << "'\n");
95479588
// If this parameter has bounds info we will tranform it into a Span,
95489589
// so then it will no longer be Escapable.
9549-
bool willBeEscapable = swiftParamTy->isEscapable() && !paramHasBoundsInfo;
9550-
printer.printLifetimeboundReturn(index, willBeEscapable);
9590+
bool willBeEscapable = swiftParamTy->isEscapable() &&
9591+
(!paramHasBoundsInfo ||
9592+
mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX);
9593+
printer.printLifetimeboundReturn(mappedIndex, willBeEscapable);
95519594
paramHasLifetimeInfo = true;
95529595
returnHasLifetimeInfo = true;
95539596
}
@@ -9563,7 +9606,6 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
95639606
}
95649607
printer.printAvailability();
95659608
printer.printTypeMapping(typeMapping);
9566-
95679609
}
95689610

95699611
if (attachMacro) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file --leading-lines %s %t
5+
6+
// RUN: %target-swift-ide-test -print-module -module-to-print=Instance -plugin-path %swift-plugin-dir -I %t/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -Xcc -Werror
7+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/Test.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -warnings-as-errors -Xcc -Werror %t/test.swift -Xllvm -debug-only=safe-interop-wrappers -Xllvm -debug-only=macros
8+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/Test.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -verify -verify-additional-file %t/Inputs/instance.h %t/test.swift -DVERIFY
9+
10+
//--- test.swift
11+
import Instance
12+
13+
@available(macOS 13.3.0, *)
14+
func foo(_ p: inout MutableSpan<CInt>, a: A, aa: inout A, c: C, d: D, dd: inout D) {
15+
aa.basic(&p)
16+
aa.bar(&p)
17+
a.constSelf(&p)
18+
a.valSelf(&p)
19+
a.wrongSelf(&p)
20+
let _: MutableSpan<CInt> = a.lifetimeBoundSelf(3)
21+
c.refSelf(&p)
22+
d.nonescaping(&p)
23+
let _: MutableSpan<CInt> = dd.nonescapingLifetimebound(73)
24+
25+
#if VERIFY
26+
aa.countedSelf(&p)
27+
a.basic(&p) // expected-error{{cannot use mutating member on immutable value: 'a' is a 'let' constant}}
28+
#endif
29+
}
30+
31+
//--- Inputs/instance.h
32+
#include <ptrcheck.h>
33+
#include <lifetimebound.h>
34+
35+
#define SWIFT_IMMORTAL_REFERENCE \
36+
__attribute__((swift_attr("import_reference"))) \
37+
__attribute__((swift_attr("retain:immortal"))) \
38+
__attribute__((swift_attr("release:immortal")))
39+
40+
#define SWIFT_NONESCAPABLE \
41+
__attribute__((swift_attr("~Escapable")))
42+
43+
struct A {};
44+
struct B { int placeholder; };
45+
struct SWIFT_IMMORTAL_REFERENCE C {};
46+
struct SWIFT_NONESCAPABLE D {};
47+
48+
void basic(struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.basic(self:_:_:)")));
49+
50+
void renamed(struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.bar(self:_:_:)")));
51+
52+
void countedSelf(struct A * __counted_by(len)
53+
a, // expected-warning{{bounds attribute '__counted_by' ignored on parameter mapped to 'self'}}
54+
int * __counted_by(len) p __noescape, int len)
55+
__attribute__((
56+
swift_name // expected-note{{swift_name maps free function to instance method here}}
57+
("A.countedSelf(self:_:_:)")));
58+
59+
void constSelf(const struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.constSelf(self:_:_:)")));
60+
61+
void valSelf(struct A a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.valSelf(self:_:_:)")));
62+
63+
void wrongSelf(struct B b, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.wrongSelf(self:_:_:)")));
64+
65+
void refSelf(struct C *c, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("C.refSelf(self:_:_:)")));
66+
67+
int * __counted_by(len) lifetimeBoundSelf(struct A a __lifetimebound, int len) __attribute__((swift_name("A.lifetimeBoundSelf(self:_:)")));
68+
69+
void nonescaping(const struct D *d, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("D.nonescaping(self:_:_:)")));
70+
71+
int * __counted_by(len) nonescapingLifetimebound(struct D *d __lifetimebound, int len) __attribute__((swift_name("D.nonescapingLifetimebound(self:_:)")));
72+
73+
//--- Inputs/module.modulemap
74+
module Instance {
75+
header "instance.h"
76+
}
77+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file --leading-lines %s %t
5+
6+
// RN: %target-swift-ide-test -print-module -module-to-print=Instance -plugin-path %swift-plugin-dir -I %t/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -Xcc -Werror -cxx-interoperability-mode=default -Xcc -std=c++20
7+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/Test.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -warnings-as-errors -Xcc -Werror %t/test.swift -Xllvm -debug-only=safe-interop-wrappers -Xllvm -debug-only=macros -cxx-interoperability-mode=default -Xcc -std=c++20
8+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/Test.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -warnings-as-errors -Xcc -Werror %t/test.swift -Xllvm -debug-only=safe-interop-wrappers -Xllvm -debug-only=macros -cxx-interoperability-mode=default -Xcc -std=c++20 -verify -verify-additional-file %t/Inputs/instance.h %t/test.swift -DVERIFY
9+
10+
//--- test.swift
11+
import Instance
12+
13+
func foo(_ p: inout MutableSpan<CInt>, a: A, aa: inout A, s: IntSpan, cs: ConstIntSpan, asp: AliasingSpan) {
14+
aa.basic(&p)
15+
aa.bar(&p)
16+
a.constSelf(&p)
17+
a.valSelf(&p)
18+
aa.refSelf(&p)
19+
20+
let _: IntSpan = unsafe s.spanSelf()
21+
let _: MutableSpan<CInt> = unsafe s.spanSelf()
22+
let _: IntSpan = unsafe s.spanConstSelf()
23+
let _: MutableSpan<CInt> = unsafe s.spanConstSelf()
24+
let _: ConstIntSpan = unsafe cs.constSpanSelf()
25+
let _: Span<CInt> = unsafe cs.constSpanSelf()
26+
let _: IntSpan = unsafe asp.spanSelf()
27+
let _: MutableSpan<CInt> = unsafe asp.spanSelf()
28+
#if VERIFY
29+
p.spanSelf() // expected-error{{value of type 'MutableSpan<CInt>' (aka 'MutableSpan<Int32>') has no member 'spanSelf'}}
30+
#endif
31+
}
32+
33+
//--- Inputs/instance.h
34+
#include <ptrcheck.h>
35+
#include <lifetimebound.h>
36+
#include <span>
37+
38+
struct A {};
39+
40+
using IntSpan = std::span<int>;
41+
using ConstIntSpan = std::span<const int>;
42+
using AliasingSpan = std::span<int>;
43+
44+
void basic(A *a, IntSpan p __noescape) __attribute__((swift_name("A.basic(self:_:)")));
45+
46+
void renamed(A *a, IntSpan p __noescape) __attribute__((swift_name("A.bar(self:_:)")));
47+
48+
void constSelf(const A *a, IntSpan p __noescape) __attribute__((swift_name("A.constSelf(self:_:)")));
49+
50+
void valSelf(A a, IntSpan p __noescape) __attribute__((swift_name("A.valSelf(self:_:)")));
51+
52+
void refSelf(A &a, IntSpan p __noescape) __attribute__((swift_name("A.refSelf(self:_:)")));
53+
54+
IntSpan spanSelf(IntSpan p __lifetimebound) __attribute__((swift_name("IntSpan.spanSelf(self:)")));
55+
56+
const IntSpan spanConstSelf(const IntSpan p __lifetimebound) __attribute__((swift_name("IntSpan.spanConstSelf(self:)")));
57+
58+
ConstIntSpan constSpanSelf(ConstIntSpan p __lifetimebound) __attribute__((swift_name("ConstIntSpan.constSpanSelf(self:)")));
59+
60+
//--- Inputs/module.modulemap
61+
module Instance {
62+
header "instance.h"
63+
export std.span
64+
}
65+

0 commit comments

Comments
 (0)