Skip to content

Commit af819b9

Browse files
committed
[ClangImporter] Attach _SwiftifyImportProtocol to imported protocols
...with bounds attributes This creates safe overloads for any methods in the protocol annotated with bounds information. Updates _SwiftifyImportProtocol to make the added overloads in the protocol public. rdar://144335990
1 parent ce27af6 commit af819b9

File tree

5 files changed

+257
-19
lines changed

5 files changed

+257
-19
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5881,6 +5881,8 @@ namespace {
58815881

58825882
result->setMemberLoader(&Impl, 0);
58835883

5884+
Impl.swiftifyProtocol(result);
5885+
58845886
return result;
58855887
}
58865888

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
18271827
void addOptionSetTypealiases(NominalTypeDecl *nominal);
18281828

18291829
void swiftify(AbstractFunctionDecl *MappedDecl);
1830+
void swiftifyProtocol(NominalTypeDecl *MappedDecl);
18301831

18311832
/// Find the lookup table that corresponds to the given Clang module.
18321833
///

lib/ClangImporter/SwiftifyDecl.cpp

Lines changed: 115 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "ImporterImpl.h"
1818
#include "swift/AST/ASTContext.h"
19+
#include "swift/AST/ASTPrinter.h"
1920
#include "swift/AST/Decl.h"
2021
#include "swift/AST/DiagnosticsClangImporter.h"
2122
#include "swift/AST/ParameterList.h"
@@ -151,6 +152,7 @@ class SwiftifyInfoPrinter {
151152
out << "\"";
152153
}
153154

155+
protected:
154156
void printSeparator() {
155157
if (!firstParam) {
156158
out << ", ";
@@ -159,6 +161,7 @@ class SwiftifyInfoPrinter {
159161
}
160162
}
161163

164+
private:
162165
void printParamOrReturn(ssize_t pointerIndex) {
163166
if (pointerIndex == SELF_PARAM_INDEX)
164167
out << ".self";
@@ -296,12 +299,25 @@ static StringRef getAttributeName(const clang::CountAttributedType *CAT) {
296299
llvm_unreachable("CountAttributedType cannot be ended_by");
297300
}
298301
}
302+
303+
template<typename T>
304+
static bool getImplicitObjectParamAnnotation(const clang::ObjCMethodDecl* D) {
305+
return false; // Only C++ methods have implicit params
306+
}
307+
308+
static size_t getNumParams(const clang::ObjCMethodDecl* D) {
309+
return D->param_size();
310+
}
311+
static size_t getNumParams(const clang::FunctionDecl* D) {
312+
return D->getNumParams();
313+
}
299314
} // namespace
300315

316+
template<typename T>
301317
static bool swiftifyImpl(ClangImporter::Implementation &Self,
302318
SwiftifyInfoPrinter &printer,
303319
const AbstractFunctionDecl *MappedDecl,
304-
const clang::FunctionDecl *ClangDecl) {
320+
const T *ClangDecl) {
305321
DLOG("Checking " << *ClangDecl << " for bounds and lifetime info\n");
306322

307323
// FIXME: for private macro generated functions we do not serialize the
@@ -310,13 +326,6 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
310326
ClangDecl->getAccess() == clang::AS_private)
311327
return false;
312328

313-
{
314-
UnaliasedInstantiationVisitor visitor;
315-
visitor.TraverseType(ClangDecl->getType());
316-
if (visitor.hasUnaliasedInstantiation)
317-
return false;
318-
}
319-
320329
clang::ASTContext &clangASTContext = Self.getClangASTContext();
321330

322331
// We only attach the macro if it will produce an overload. Any __counted_by
@@ -332,9 +341,10 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
332341
swiftReturnTy = ctorDecl->getResultInterfaceType();
333342
else
334343
ABORT("Unexpected AbstractFunctionDecl subclass.");
344+
clang::QualType clangReturnTy = ClangDecl->getReturnType();
335345
bool returnIsStdSpan = printer.registerStdSpanTypeMapping(
336-
swiftReturnTy, ClangDecl->getReturnType());
337-
auto *CAT = ClangDecl->getReturnType()->getAs<clang::CountAttributedType>();
346+
swiftReturnTy, clangReturnTy);
347+
auto *CAT = clangReturnTy->getAs<clang::CountAttributedType>();
338348
if (SwiftifiableCAT(clangASTContext, CAT, swiftReturnTy)) {
339349
printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX);
340350
DLOG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n");
@@ -361,13 +371,13 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
361371
size_t swiftNumParams = MappedDecl->getParameters()->size() -
362372
(ClangDecl->isVariadic() ? 1 : 0);
363373
ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) ==
364-
(ClangDecl->getNumParams() == swiftNumParams));
374+
(getNumParams(ClangDecl) == swiftNumParams));
365375

366376
size_t selfParamIndex = MappedDecl->isImportAsInstanceMember()
367377
? MappedDecl->getSelfIndex()
368-
: ClangDecl->getNumParams();
378+
: getNumParams(ClangDecl);
369379
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
370-
auto clangParamTy = clangParam->getType();
380+
clang::QualType clangParamTy = clangParam->getType();
371381
int mappedIndex = index < selfParamIndex ? index :
372382
index > selfParamIndex ? index - 1 :
373383
SwiftifyInfoPrinter::SELF_PARAM_INDEX;
@@ -383,13 +393,12 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
383393
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
384394
if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
385395
Self.diagnose(HeaderLoc(clangParam->getLocation()),
386-
diag::warn_clang_ignored_bounds_on_self,
387-
getAttributeName(CAT));
388-
auto swiftName = ClangDecl->getAttr<clang::SwiftNameAttr>();
396+
diag::warn_clang_ignored_bounds_on_self, getAttributeName(CAT));
397+
auto swiftName = ClangDecl->template getAttr<clang::SwiftNameAttr>();
389398
ASSERT(swiftName &&
390399
"free function mapped to instance method without swift_name??");
391400
Self.diagnose(HeaderLoc(swiftName->getLocation()),
392-
diag::note_swift_name_instance_method);
401+
diag::note_swift_name_instance_method);
393402
} else if (SwiftifiableCAT(clangASTContext, CAT, swiftParamTy)) {
394403
printer.printCountedBy(CAT, mappedIndex);
395404
DLOG(" Found bounds info '" << clangParamTy << "' on parameter '"
@@ -401,7 +410,7 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
401410
paramHasBoundsInfo |= paramIsStdSpan;
402411

403412
bool paramHasLifetimeInfo = false;
404-
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
413+
if (clangParam->template hasAttr<clang::NoEscapeAttr>()) {
405414
DLOG(" Found noescape attribute on parameter '" << *clangParam << "'\n");
406415
printer.printNonEscaping(mappedIndex);
407416
paramHasLifetimeInfo = true;
@@ -435,6 +444,47 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
435444
return attachMacro;
436445
}
437446

447+
class SwiftifyProtocolInfoPrinter : public SwiftifyInfoPrinter {
448+
private:
449+
ClangImporter::Implementation &ImporterImpl;
450+
451+
public:
452+
SwiftifyProtocolInfoPrinter(ClangImporter::Implementation &ImporterImpl,
453+
clang::ASTContext &ctx, ASTContext &SwiftContext,
454+
llvm::raw_ostream &out,
455+
MacroDecl &SwiftifyImportDecl)
456+
: SwiftifyInfoPrinter(ctx, SwiftContext, out, SwiftifyImportDecl),
457+
ImporterImpl(ImporterImpl) {}
458+
459+
bool printMethod(const FuncDecl *Method) {
460+
// don't emit .method() if we know it's going to be empty
461+
auto ClangDecl = dyn_cast_or_null<clang::ObjCMethodDecl>(Method->getClangDecl());
462+
if (!ClangDecl)
463+
return false;
464+
465+
printSeparator();
466+
out << ".method(signature: \"";
467+
printMethodSignature(Method);
468+
out << "\", paramInfo: [";
469+
// reset firstParam inside paramInfo array. At this point firstParam will
470+
// always be false, so no need to save the current value.
471+
assert(!firstParam);
472+
firstParam = true;
473+
bool hadAttributes = swiftifyImpl(ImporterImpl, *this, Method, ClangDecl);
474+
firstParam = false;
475+
out << "])";
476+
return hadAttributes;
477+
}
478+
479+
private:
480+
void printMethodSignature(const FuncDecl *Method) {
481+
auto options =
482+
PrintOptions::printForDiagnostics(AccessLevel::Private, true);
483+
StreamPrinter printer(out);
484+
Method->print(printer, options);
485+
}
486+
};
487+
438488
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
439489
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) ||
440490
SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers)
@@ -443,6 +493,13 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
443493
if (!ClangDecl)
444494
return;
445495

496+
{
497+
UnaliasedInstantiationVisitor visitor;
498+
visitor.TraverseType(ClangDecl->getType());
499+
if (visitor.hasUnaliasedInstantiation)
500+
return;
501+
}
502+
446503
MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(getKnownSingleDecl(SwiftContext, "_SwiftifyImport"));
447504
if (!SwiftifyImportDecl)
448505
return;
@@ -479,3 +536,43 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
479536
}
480537
}
481538

539+
void ClangImporter::Implementation::swiftifyProtocol(
540+
NominalTypeDecl *MappedDecl) {
541+
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) ||
542+
SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers)
543+
return;
544+
if (!isa<ProtocolDecl, ClassDecl>(MappedDecl))
545+
return;
546+
547+
MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(
548+
getKnownSingleDecl(SwiftContext, "_SwiftifyImportProtocol"));
549+
if (!SwiftifyImportDecl)
550+
return;
551+
552+
DLOG("Checking " << MappedDecl->getName() << " protocol for methods with bounds and lifetime info\n");
553+
llvm::SmallString<128> MacroString;
554+
{
555+
llvm::raw_svector_ostream out(MacroString);
556+
out << "@_SwiftifyImportProtocol";
557+
558+
bool hasBoundsAttributes = false;
559+
SwiftifyProtocolInfoPrinter printer(*this, getClangASTContext(),
560+
SwiftContext, out, *SwiftifyImportDecl);
561+
for (Decl *SwiftMember :
562+
cast<IterableDeclContext>(MappedDecl)->getAllMembers()) {
563+
FuncDecl *SwiftDecl = dyn_cast<FuncDecl>(SwiftMember);
564+
if (!SwiftDecl)
565+
continue;
566+
hasBoundsAttributes |= printer.printMethod(SwiftDecl);
567+
}
568+
569+
if (!hasBoundsAttributes)
570+
return;
571+
printer.printAvailability();
572+
printer.printTypeMapping();
573+
}
574+
575+
DLOG("Attaching safe interop macro: " << MacroString << "\n");
576+
importNontrivialAttribute(MappedDecl, MacroString);
577+
}
578+

lib/Sema/TypeCheckMacros.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,7 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) {
13601360
return macroSourceFile->getBufferID();
13611361
}
13621362

1363+
13631364
static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
13641365
CustomAttr *attr,
13651366
bool passParentContext, MacroRole role,
@@ -1370,7 +1371,11 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
13701371
dc = attachedTo->getDeclContext();
13711372
} else if (role == MacroRole::Conformance || role == MacroRole::Extension) {
13721373
// Conformance macros always expand to extensions at file-scope.
1373-
dc = attachedTo->getDeclContext()->getParentSourceFile();
1374+
dc = attachedTo->getDeclContext();
1375+
if (!isa<ClangModuleUnit>(dc->getModuleScopeContext()))
1376+
dc = dc->getParentSourceFile();
1377+
else // decls imported from clang do not have a SourceFile
1378+
ASSERT(isa<FileUnit>(dc) && !isa<SourceFile>(dc));
13741379
} else {
13751380
dc = attachedTo->getInnermostDeclContext();
13761381
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file %s %t
5+
6+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByProtocol.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers %t/counted-by-protocol.swift -verify -Xcc -Wno-nullability-completeness
7+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByProtocol.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers %t/counted-by-protocol.swift -dump-macro-expansions 2> %t/expansions.out
8+
// RUN: %diff %t/expansions.out %t/expansions.expected
9+
10+
//--- Inputs/module.modulemap
11+
module CountedByProtocolClang {
12+
header "counted-by-protocol.h"
13+
export *
14+
}
15+
16+
//--- Inputs/counted-by-protocol.h
17+
#pragma once
18+
19+
#define __counted_by(x) __attribute__((__counted_by__(x)))
20+
21+
int foo(int len, int * __counted_by(len) p);
22+
@protocol CountedByProtocol
23+
- (void) simple:(int)len :(int * __counted_by(len))p;
24+
- (void) shared:(int)len :(int * __counted_by(len))p1 :(int * __counted_by(len))p2;
25+
- (void) complexExpr:(int)len :(int) offset :(int * __counted_by(len - offset))p;
26+
- (void) nullUnspecified:(int)len :(int * __counted_by(len) _Null_unspecified)p;
27+
- (void) nonnull:(int)len :(int * __counted_by(len) _Nonnull)p;
28+
- (void) nullable:(int)len :(int * __counted_by(len) _Nullable)p;
29+
- (int * __counted_by(len)) returnPointer:(int)len;
30+
31+
+ (void) staticMethod:(int)len :(int * __counted_by(len))p;
32+
@end
33+
34+
__attribute__((swift_attr("@_SwiftifyImportProtocol(.method(signature: \"func swiftAttr(_ len: Int32, _ p: UnsafeMutablePointer<Int32>!)\", paramInfo: [.countedBy(pointer: .param(2), count: \"len\")]))")))
35+
@protocol SwiftAttrProtocol
36+
- (void)swiftAttr:(int)len :(int *)p;
37+
@end
38+
39+
//--- expansions.expected
40+
@__swiftmacro_So17CountedByProtocol015_SwiftifyImportC0fMe_.swift
41+
------------------------------
42+
extension CountedByProtocol {
43+
/// This is an auto-generated wrapper for safer interop
44+
@_alwaysEmitIntoClient @_disfavoredOverload public
45+
func simple(_ p: UnsafeMutableBufferPointer<Int32>) {
46+
let len = Int32(exactly: p.count)!
47+
return unsafe simple(len, p.baseAddress!)
48+
}
49+
/// This is an auto-generated wrapper for safer interop
50+
@_alwaysEmitIntoClient @_disfavoredOverload public
51+
func shared(_ p1: UnsafeMutableBufferPointer<Int32>, _ p2: UnsafeMutableBufferPointer<Int32>) {
52+
let len = Int32(exactly: p1.count)!
53+
if p2.count != len {
54+
fatalError("bounds check failure in shared: expected \(len) but got \(p2.count)")
55+
}
56+
return unsafe shared(len, p1.baseAddress!, p2.baseAddress!)
57+
}
58+
/// This is an auto-generated wrapper for safer interop
59+
@_alwaysEmitIntoClient @_disfavoredOverload public
60+
func complexExpr(_ len: Int32, _ offset: Int32, _ p: UnsafeMutableBufferPointer<Int32>) {
61+
let _pCount = p.count
62+
if _pCount != len - offset {
63+
fatalError("bounds check failure in complexExpr: expected \(len - offset) but got \(_pCount)")
64+
}
65+
return unsafe complexExpr(len, offset, p.baseAddress!)
66+
}
67+
/// This is an auto-generated wrapper for safer interop
68+
@_alwaysEmitIntoClient @_disfavoredOverload public
69+
func nullUnspecified(_ p: UnsafeMutableBufferPointer<Int32>) {
70+
let len = Int32(exactly: p.count)!
71+
return unsafe nullUnspecified(len, p.baseAddress!)
72+
}
73+
/// This is an auto-generated wrapper for safer interop
74+
@_alwaysEmitIntoClient @_disfavoredOverload public
75+
func nonnull(_ p: UnsafeMutableBufferPointer<Int32>) {
76+
let len = Int32(exactly: p.count)!
77+
return unsafe nonnull(len, p.baseAddress!)
78+
}
79+
/// This is an auto-generated wrapper for safer interop
80+
@_alwaysEmitIntoClient @_disfavoredOverload public
81+
func nullable(_ p: UnsafeMutableBufferPointer<Int32>?) {
82+
let len = Int32(exactly: unsafe p?.count ?? 0)!
83+
return unsafe nullable(len, p?.baseAddress)
84+
}
85+
/// This is an auto-generated wrapper for safer interop
86+
@_alwaysEmitIntoClient @_disfavoredOverload public
87+
func returnPointer(_ len: Int32) -> UnsafeMutableBufferPointer<Int32> {
88+
return unsafe UnsafeMutableBufferPointer<Int32>(start: unsafe returnPointer(len), count: Int(len))
89+
}
90+
/// This is an auto-generated wrapper for safer interop
91+
@_alwaysEmitIntoClient @_disfavoredOverload
92+
static public func staticMethod(_ p: UnsafeMutableBufferPointer<Int32>) {
93+
let len = Int32(exactly: p.count)!
94+
return unsafe staticMethod(len, p.baseAddress!)
95+
}
96+
}
97+
------------------------------
98+
@__swiftmacro_So17SwiftAttrProtocol015_SwiftifyImportC0fMe_.swift
99+
------------------------------
100+
extension SwiftAttrProtocol {
101+
/// This is an auto-generated wrapper for safer interop
102+
@_alwaysEmitIntoClient @_disfavoredOverload public
103+
func swiftAttr(_ p: UnsafeMutableBufferPointer<Int32>) {
104+
let len = Int32(exactly: p.count)!
105+
return unsafe swiftAttr(len, p.baseAddress!)
106+
}
107+
}
108+
------------------------------
109+
@__swiftmacro_So3foo15_SwiftifyImportfMp_.swift
110+
------------------------------
111+
/// This is an auto-generated wrapper for safer interop
112+
@_alwaysEmitIntoClient @_disfavoredOverload public func foo(_ p: UnsafeMutableBufferPointer<Int32>) -> Int32 {
113+
let len = Int32(exactly: p.count)!
114+
return unsafe foo(len, p.baseAddress!)
115+
}
116+
------------------------------
117+
//--- counted-by-protocol.swift
118+
import CountedByProtocolClang
119+
120+
@inlinable
121+
public func call(p: UnsafeMutableBufferPointer<CInt>, x: CInt, y: CInt, a: CountedByProtocol, b: SwiftAttrProtocol) {
122+
a.simple(p)
123+
a.shared(p, p)
124+
a.complexExpr(x, y, p)
125+
a.nullUnspecified(p)
126+
a.nonnull(p)
127+
a.nullable(p)
128+
let _: UnsafeMutableBufferPointer<CInt> = a.returnPointer(x)
129+
let r2 = a.returnPointer(x)
130+
let _: UnsafeMutablePointer<CInt>? = r2 // make sure the original is the favored overload
131+
b.swiftAttr(p)
132+
let _ = foo(p)
133+
}

0 commit comments

Comments
 (0)