diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 17dc9aa1a7194..8d6c763018baa 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -5881,6 +5881,8 @@ namespace { result->setMemberLoader(&Impl, 0); + Impl.swiftifyProtocol(result); + return result; } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index e17f4d8d20d6e..fef4067233d05 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1827,6 +1827,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation void addOptionSetTypealiases(NominalTypeDecl *nominal); void swiftify(AbstractFunctionDecl *MappedDecl); + void swiftifyProtocol(NominalTypeDecl *MappedDecl); /// Find the lookup table that corresponds to the given Clang module. /// diff --git a/lib/ClangImporter/SwiftifyDecl.cpp b/lib/ClangImporter/SwiftifyDecl.cpp index 7be08be0032a9..f0e0a5ed81588 100644 --- a/lib/ClangImporter/SwiftifyDecl.cpp +++ b/lib/ClangImporter/SwiftifyDecl.cpp @@ -16,6 +16,7 @@ #include "ImporterImpl.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/ParameterList.h" @@ -36,29 +37,87 @@ namespace { ValueDecl *getKnownSingleDecl(ASTContext &SwiftContext, StringRef DeclName) { SmallVector decls; SwiftContext.lookupInSwiftModule(DeclName, decls); - assert(decls.size() < 2); + ASSERT(decls.size() < 2); if (decls.size() != 1) return nullptr; return decls[0]; } -class SwiftifyInfoPrinter { -public: +struct SwiftifyInfoPrinter { static const ssize_t SELF_PARAM_INDEX = -2; static const ssize_t RETURN_VALUE_INDEX = -1; clang::ASTContext &ctx; ASTContext &SwiftContext; - llvm::raw_ostream &out; + llvm::raw_svector_ostream &out; MacroDecl &SwiftifyImportDecl; bool firstParam = true; - llvm::StringMap typeMapping; + llvm::StringMap &typeMapping; +protected: SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, - llvm::raw_ostream &out, MacroDecl &SwiftifyImportDecl) + llvm::raw_svector_ostream &out, + MacroDecl &SwiftifyImportDecl, + llvm::StringMap &typeMapping) : ctx(ctx), SwiftContext(SwiftContext), out(out), - SwiftifyImportDecl(SwiftifyImportDecl) { - out << "("; + SwiftifyImportDecl(SwiftifyImportDecl), typeMapping(typeMapping) {} + +public: + void printTypeMapping() { + printSeparator(); + out << "typeMappings: ["; + if (typeMapping.empty()) { + out << ":]"; + return; + } + llvm::interleaveComma(typeMapping, out, [&](const auto &entry) { + out << '"' << entry.getKey() << "\" : \"" << entry.getValue() << '"'; + }); + out << "]"; + } + + void printAvailability() { + if (!hasMacroParameter("spanAvailability")) + return; + printSeparator(); + out << "spanAvailability: "; + printAvailabilityOfType("Span"); + } +private: + bool hasMacroParameter(StringRef ParamName) { + for (auto *Param : *SwiftifyImportDecl.parameterList) + if (Param->getArgumentName().str() == ParamName) + return true; + return false; } - ~SwiftifyInfoPrinter() { out << ")"; } + + void printAvailabilityOfType(StringRef Name) { + ValueDecl *D = getKnownSingleDecl(SwiftContext, Name); + out << "\""; + llvm::SaveAndRestore hasAvailbilitySeparatorRestore(firstParam, true); + for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) { + auto introducedOpt = attr.getIntroduced(); + if (!introducedOpt.has_value()) continue; + printSeparator(); + out << prettyPlatformString(attr.getPlatform()) << " " << introducedOpt.value(); + } + out << "\""; + } + +protected: + void printSeparator() { + if (!firstParam) { + out << ", "; + } else { + firstParam = false; + } + } +}; + +struct SwiftifyInfoFunctionPrinter : public SwiftifyInfoPrinter { + SwiftifyInfoFunctionPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, + llvm::raw_svector_ostream &out, + MacroDecl &SwiftifyImportDecl, + llvm::StringMap &typeMapping) + : SwiftifyInfoPrinter(ctx, SwiftContext, out, SwiftifyImportDecl, typeMapping) {} void printCountedBy(const clang::CountAttributedType *CAT, ssize_t pointerIndex) { @@ -109,56 +168,7 @@ class SwiftifyInfoPrinter { return false; } - void printTypeMapping() { - printSeparator(); - out << "typeMappings: ["; - if (typeMapping.empty()) { - out << ":]"; - return; - } - llvm::interleaveComma(typeMapping, out, [&](const auto &entry) { - out << '"' << entry.getKey() << "\" : \"" << entry.getValue() << '"'; - }); - out << "]"; - } - - void printAvailability() { - if (!hasMacroParameter("spanAvailability")) - return; - printSeparator(); - out << "spanAvailability: "; - printAvailabilityOfType("Span"); - } - private: - bool hasMacroParameter(StringRef ParamName) { - for (auto *Param : *SwiftifyImportDecl.parameterList) - if (Param->getArgumentName().str() == ParamName) - return true; - return false; - } - - void printAvailabilityOfType(StringRef Name) { - ValueDecl *D = getKnownSingleDecl(SwiftContext, Name); - out << "\""; - llvm::SaveAndRestore hasAvailbilitySeparatorRestore(firstParam, true); - for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) { - auto introducedOpt = attr.getIntroduced(); - if (!introducedOpt.has_value()) continue; - printSeparator(); - out << prettyPlatformString(attr.getPlatform()) << " " << introducedOpt.value(); - } - out << "\""; - } - - void printSeparator() { - if (!firstParam) { - out << ", "; - } else { - firstParam = false; - } - } - void printParamOrReturn(ssize_t pointerIndex) { if (pointerIndex == SELF_PARAM_INDEX) out << ".self"; @@ -296,12 +306,25 @@ static StringRef getAttributeName(const clang::CountAttributedType *CAT) { llvm_unreachable("CountAttributedType cannot be ended_by"); } } + +template +static bool getImplicitObjectParamAnnotation(const clang::ObjCMethodDecl* D) { + return false; // Only C++ methods have implicit params +} + +static size_t getNumParams(const clang::ObjCMethodDecl* D) { + return D->param_size(); +} +static size_t getNumParams(const clang::FunctionDecl* D) { + return D->getNumParams(); +} } // namespace +template static bool swiftifyImpl(ClangImporter::Implementation &Self, - SwiftifyInfoPrinter &printer, + SwiftifyInfoFunctionPrinter &printer, const AbstractFunctionDecl *MappedDecl, - const clang::FunctionDecl *ClangDecl) { + const T *ClangDecl) { DLOG("Checking " << *ClangDecl << " for bounds and lifetime info\n"); // FIXME: for private macro generated functions we do not serialize the @@ -310,13 +333,6 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, ClangDecl->getAccess() == clang::AS_private) return false; - { - UnaliasedInstantiationVisitor visitor; - visitor.TraverseType(ClangDecl->getType()); - if (visitor.hasUnaliasedInstantiation) - return false; - } - clang::ASTContext &clangASTContext = Self.getClangASTContext(); // We only attach the macro if it will produce an overload. Any __counted_by @@ -332,9 +348,10 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, swiftReturnTy = ctorDecl->getResultInterfaceType(); else ABORT("Unexpected AbstractFunctionDecl subclass."); + clang::QualType clangReturnTy = ClangDecl->getReturnType(); bool returnIsStdSpan = printer.registerStdSpanTypeMapping( - swiftReturnTy, ClangDecl->getReturnType()); - auto *CAT = ClangDecl->getReturnType()->getAs(); + swiftReturnTy, clangReturnTy); + auto *CAT = clangReturnTy->getAs(); if (SwiftifiableCAT(clangASTContext, CAT, swiftReturnTy)) { printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX); DLOG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); @@ -361,13 +378,13 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, size_t swiftNumParams = MappedDecl->getParameters()->size() - (ClangDecl->isVariadic() ? 1 : 0); ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) == - (ClangDecl->getNumParams() == swiftNumParams)); + (getNumParams(ClangDecl) == swiftNumParams)); size_t selfParamIndex = MappedDecl->isImportAsInstanceMember() ? MappedDecl->getSelfIndex() - : ClangDecl->getNumParams(); + : getNumParams(ClangDecl); for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) { - auto clangParamTy = clangParam->getType(); + clang::QualType clangParamTy = clangParam->getType(); int mappedIndex = index < selfParamIndex ? index : index > selfParamIndex ? index - 1 : SwiftifyInfoPrinter::SELF_PARAM_INDEX; @@ -383,13 +400,12 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, auto *CAT = clangParamTy->getAs(); if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) { Self.diagnose(HeaderLoc(clangParam->getLocation()), - diag::warn_clang_ignored_bounds_on_self, - getAttributeName(CAT)); - auto swiftName = ClangDecl->getAttr(); + diag::warn_clang_ignored_bounds_on_self, getAttributeName(CAT)); + auto swiftName = ClangDecl->template getAttr(); ASSERT(swiftName && "free function mapped to instance method without swift_name??"); Self.diagnose(HeaderLoc(swiftName->getLocation()), - diag::note_swift_name_instance_method); + diag::note_swift_name_instance_method); } else if (SwiftifiableCAT(clangASTContext, CAT, swiftParamTy)) { printer.printCountedBy(CAT, mappedIndex); DLOG(" Found bounds info '" << clangParamTy << "' on parameter '" @@ -401,7 +417,7 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, paramHasBoundsInfo |= paramIsStdSpan; bool paramHasLifetimeInfo = false; - if (clangParam->hasAttr()) { + if (clangParam->template hasAttr()) { DLOG(" Found noescape attribute on parameter '" << *clangParam << "'\n"); printer.printNonEscaping(mappedIndex); paramHasLifetimeInfo = true; @@ -435,6 +451,48 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, return attachMacro; } +class SwiftifyProtocolInfoPrinter : public SwiftifyInfoPrinter { +private: + ClangImporter::Implementation &ImporterImpl; + +public: + SwiftifyProtocolInfoPrinter(ClangImporter::Implementation &ImporterImpl, + clang::ASTContext &ctx, ASTContext &SwiftContext, + llvm::raw_svector_ostream &out, + MacroDecl &SwiftifyImportDecl, + llvm::StringMap &typeMapping) + : SwiftifyInfoPrinter(ctx, SwiftContext, out, SwiftifyImportDecl, typeMapping), + ImporterImpl(ImporterImpl) {} + + bool printMethod(const FuncDecl *Method) { + auto ClangDecl = dyn_cast_or_null(Method->getClangDecl()); + if (!ClangDecl) + return false; + + llvm::SmallString<128> paramInfoString; + llvm::raw_svector_ostream tmpOut(paramInfoString); + + SwiftifyInfoFunctionPrinter methodPrinter(ctx, SwiftContext, tmpOut, + SwiftifyImportDecl, typeMapping); + bool hadAttributes = swiftifyImpl(ImporterImpl, methodPrinter, Method, ClangDecl); + if (hadAttributes) { + printSeparator(); + out << ".method(signature: \""; + printMethodSignature(Method); + out << "\", paramInfo: [" << paramInfoString << "])"; + } + return hadAttributes; + } + +private: + void printMethodSignature(const FuncDecl *Method) { + auto options = + PrintOptions::printForDiagnostics(AccessLevel::Private, true); + StreamPrinter printer(out); + Method->print(printer, options); + } +}; + void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) || SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers) @@ -443,6 +501,13 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { if (!ClangDecl) return; + { + UnaliasedInstantiationVisitor visitor; + visitor.TraverseType(ClangDecl->getType()); + if (visitor.hasUnaliasedInstantiation) + return; + } + MacroDecl *SwiftifyImportDecl = dyn_cast_or_null(getKnownSingleDecl(SwiftContext, "_SwiftifyImport")); if (!SwiftifyImportDecl) return; @@ -450,13 +515,16 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { llvm::SmallString<128> MacroString; { llvm::raw_svector_ostream out(MacroString); - out << "@_SwiftifyImport"; + out << "@_SwiftifyImport("; - SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out, *SwiftifyImportDecl); + llvm::StringMap typeMapping; + SwiftifyInfoFunctionPrinter printer(getClangASTContext(), SwiftContext, out, + *SwiftifyImportDecl, typeMapping); if (!swiftifyImpl(*this, printer, MappedDecl, ClangDecl)) return; printer.printAvailability(); printer.printTypeMapping(); + out << ")"; } DLOG("Attaching safe interop macro: " << MacroString << "\n"); @@ -479,3 +547,46 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { } } +void ClangImporter::Implementation::swiftifyProtocol( + NominalTypeDecl *MappedDecl) { + if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) || + SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers) + return; + if (!isa(MappedDecl)) + return; + + MacroDecl *SwiftifyImportDecl = dyn_cast_or_null( + getKnownSingleDecl(SwiftContext, "_SwiftifyImportProtocol")); + if (!SwiftifyImportDecl) + return; + + DLOG("Checking " << MappedDecl->getName() << " protocol for methods with bounds and lifetime info\n"); + llvm::SmallString<128> MacroString; + { + llvm::raw_svector_ostream out(MacroString); + out << "@_SwiftifyImportProtocol("; + + bool hasBoundsAttributes = false; + llvm::StringMap typeMapping; + SwiftifyProtocolInfoPrinter printer(*this, getClangASTContext(), + SwiftContext, out, *SwiftifyImportDecl, + typeMapping); + for (Decl *SwiftMember : + cast(MappedDecl)->getAllMembers()) { + FuncDecl *SwiftDecl = dyn_cast(SwiftMember); + if (!SwiftDecl) + continue; + hasBoundsAttributes |= printer.printMethod(SwiftDecl); + } + + if (!hasBoundsAttributes) + return; + printer.printAvailability(); + printer.printTypeMapping(); + out << ")"; + } + + DLOG("Attaching safe interop macro: " << MacroString << "\n"); + importNontrivialAttribute(MappedDecl, MacroString); +} + diff --git a/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift b/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift index ea5af950daf3a..461807dbe4847 100644 --- a/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift @@ -1808,11 +1808,21 @@ public struct SwiftifyImportProtocolMacro: ExtensionMacro { } let overloads = try arguments.map { let (method, args) = try parseProtocolMacroParam($0, methods: methods) - let function = try constructOverloadFunction( + let hasVisibilityModifier = method.modifiers.contains { modifier in + let modName = modifier.name.trimmed.text + return modName == "public" || modName == "internal" || modName == "open" + || modName == "private" || modName == "filePrivate" + } + let result = try constructOverloadFunction( forDecl: method, leadingTrivia: Trivia(), args: args, spanAvailability: spanAvailability, typeMappings: typeMappings) - return MemberBlockItemSyntax(decl: function) + guard let newMethod = result.as(FunctionDeclSyntax.self)? + .with(\.modifiers, method.modifiers + + (hasVisibilityModifier ? [] : [DeclModifierSyntax(name: .identifier("public"))])) else { + throw RuntimeError("expected FunctionDeclSyntax but got \(result.kind) for \(method.description)") + } + return MemberBlockItemSyntax(decl: newMethod) } return [ExtensionDeclSyntax(extensionKeyword: .identifier("extension"), extendedType: type, diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 9e57b5a012a13..6df797d829003 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -1360,6 +1360,7 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) { return macroSourceFile->getBufferID(); } + static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, bool passParentContext, MacroRole role, @@ -1370,7 +1371,11 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, dc = attachedTo->getDeclContext(); } else if (role == MacroRole::Conformance || role == MacroRole::Extension) { // Conformance macros always expand to extensions at file-scope. - dc = attachedTo->getDeclContext()->getParentSourceFile(); + dc = attachedTo->getDeclContext(); + if (!isa(dc->getModuleScopeContext())) + dc = dc->getParentSourceFile(); + else // decls imported from clang do not have a SourceFile + ASSERT(isa(dc) && !isa(dc)); } else { dc = attachedTo->getInnermostDeclContext(); } diff --git a/test/Interop/ObjC/swiftify-import/counted-by-protocol-noescape.swift b/test/Interop/ObjC/swiftify-import/counted-by-protocol-noescape.swift new file mode 100644 index 0000000000000..480548ddab18e --- /dev/null +++ b/test/Interop/ObjC/swiftify-import/counted-by-protocol-noescape.swift @@ -0,0 +1,178 @@ +// REQUIRES: swift_feature_SafeInteropWrappers +// REQUIRES: swift_feature_LifetimeDependence + +// REQUIRES: foundation + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %t/test.swift -verify -Xcc -Wno-nullability-completeness +// RUN: env SWIFT_BACKTRACE="" %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %t/test.swift -dump-macro-expansions 2> %t/expansions.out +// RUN: %diff %t/expansions.out %t/expansions.expected + +//--- test.h +#pragma once + +#include + +// __counted_by definition inherited from Foundation.h +#define __noescape __attribute__((noescape)) +#define __lifetimebound __attribute__((lifetimebound)) + +void foo(int len, int * __counted_by(len) p __noescape); +@protocol TestProtocol + - (void) bar:(int * __noescape)p _Nullable:(int)asdf; + - (void) simple:(int)len :(int * __counted_by(len) __noescape)p; + - (void) shared:(int)len :(int * __counted_by(len) __noescape)p1 :(int * __counted_by(len) __noescape)p2; + - (void) complexExpr:(int)len :(int) offset :(int * __counted_by(len - offset) __noescape)p; + - (void) nullUnspecified:(int)len :(int * __counted_by(len) _Null_unspecified __noescape)p; + - (void) nonnull:(int)len :(int * __counted_by(len) _Nonnull __noescape)p; + - (void) nullable:(int)len :(int * __counted_by(len) _Nullable __noescape)p; + - (int * __counted_by(len)) returnPointer:(int)len : (int * __counted_by(len) _Nullable) __lifetimebound p; + - (void) mixedEscapability:(int)len :(int * __counted_by(len) __noescape)p1 :(int * __counted_by(len))p2; +@end + +@protocol StaticProtocol + + (void) staticMethod:(int)len :(int * __counted_by(len) __noescape)p; +@end + +@interface StaticInterface : NSObject {} +@end + +//--- expansions.expected +@__swiftmacro_So12TestProtocol015_SwiftifyImportB0fMe_.swift +------------------------------ +extension TestProtocol { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public + func simple(_ p: inout MutableSpan) { + let len = Int32(exactly: p.count)! + return unsafe p.withUnsafeMutableBufferPointer { _pPtr in + return unsafe simple(len, _pPtr.baseAddress!) + } + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p1: copy p1) @_lifetime(p2: copy p2) @_disfavoredOverload public + func shared(_ p1: inout MutableSpan, _ p2: inout MutableSpan) { + let len = Int32(exactly: p1.count)! + if p2.count != len { + fatalError("bounds check failure in shared: expected \(len) but got \(p2.count)") + } + return unsafe p2.withUnsafeMutableBufferPointer { _p2Ptr in + return unsafe p1.withUnsafeMutableBufferPointer { _p1Ptr in + return unsafe shared(len, _p1Ptr.baseAddress!, _p2Ptr.baseAddress!) + } + } + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public + func complexExpr(_ len: Int32, _ offset: Int32, _ p: inout MutableSpan) { + let _pCount = p.count + if _pCount != len - offset { + fatalError("bounds check failure in complexExpr: expected \(len - offset) but got \(_pCount)") + } + return unsafe p.withUnsafeMutableBufferPointer { _pPtr in + return unsafe complexExpr(len, offset, _pPtr.baseAddress!) + } + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public + func nullUnspecified(_ p: inout MutableSpan) { + let len = Int32(exactly: p.count)! + return unsafe p.withUnsafeMutableBufferPointer { _pPtr in + return unsafe nullUnspecified(len, _pPtr.baseAddress!) + } + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public + func nonnull(_ p: inout MutableSpan) { + let len = Int32(exactly: p.count)! + return unsafe p.withUnsafeMutableBufferPointer { _pPtr in + return unsafe nonnull(len, _pPtr.baseAddress!) + } + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public + func nullable(_ p: inout MutableSpan?) { + let len = Int32(exactly: p?.count ?? 0)! + return { () in + return if p == nil { + unsafe nullable(len, nil) + } else { + unsafe p!.withUnsafeMutableBufferPointer { _pPtr in + return unsafe nullable(len, _pPtr.baseAddress) + } + } + }() + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(copy p) @_lifetime(p: copy p) @_disfavoredOverload public + func returnPointer(_ p: inout MutableSpan?) -> MutableSpan { + let len = Int32(exactly: p?.count ?? 0)! + return unsafe _swiftifyOverrideLifetime(MutableSpan(_unsafeStart: { () in + return if p == nil { + unsafe returnPointer(len, nil) + } else { + unsafe p!.withUnsafeMutableBufferPointer { _pPtr in + return unsafe returnPointer(len, _pPtr.baseAddress) + } + } + }(), count: Int(len)), copying: ()) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p1: copy p1) @_disfavoredOverload public + func mixedEscapability(_ p1: inout MutableSpan, _ p2: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p1.count)! + if p2.count != len { + fatalError("bounds check failure in mixedEscapability: expected \(len) but got \(p2.count)") + } + return unsafe p1.withUnsafeMutableBufferPointer { _p1Ptr in + return unsafe mixedEscapability(len, _p1Ptr.baseAddress!, p2.baseAddress!) + } + } +} +------------------------------ +@__swiftmacro_So14StaticProtocol015_SwiftifyImportB0fMe_.swift +------------------------------ +extension StaticProtocol { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload + static public func staticMethod(_ p: inout MutableSpan) { + let len = Int32(exactly: p.count)! + return unsafe p.withUnsafeMutableBufferPointer { _pPtr in + return unsafe staticMethod(len, _pPtr.baseAddress!) + } + } +} +------------------------------ +@__swiftmacro_So3foo15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public func foo(_ p: inout MutableSpan) { + let len = Int32(exactly: p.count)! + return unsafe p.withUnsafeMutableBufferPointer { _pPtr in + return unsafe foo(len, _pPtr.baseAddress!) + } +} +------------------------------ +//--- test.swift +import TestClang + +@inlinable +public func call(p: inout MutableSpan, p2: inout MutableSpan, p3: inout MutableSpan?, a: TestProtocol) { + a.simple(&p) + a.shared(&p, &p2) + a.complexExpr(2, 3, &p) + a.nullUnspecified(&p) + a.nonnull(&p) + a.nullable(&p3) + let _: MutableSpan = a.returnPointer(&p3) + StaticInterface.staticMethod(&p2) + foo(&p) +} + +//--- module.modulemap +module TestClang { + header "test.h" + export * +} diff --git a/test/Interop/ObjC/swiftify-import/counted-by-protocol.swift b/test/Interop/ObjC/swiftify-import/counted-by-protocol.swift new file mode 100644 index 0000000000000..a15dee63f3063 --- /dev/null +++ b/test/Interop/ObjC/swiftify-import/counted-by-protocol.swift @@ -0,0 +1,133 @@ +// REQUIRES: swift_feature_SafeInteropWrappers + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// 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 +// RUN: env SWIFT_BACKTRACE="" %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 +// RUN: %diff %t/expansions.out %t/expansions.expected + +//--- Inputs/module.modulemap +module CountedByProtocolClang { + header "counted-by-protocol.h" + export * +} + +//--- Inputs/counted-by-protocol.h +#pragma once + +#define __counted_by(x) __attribute__((__counted_by__(x))) + +int foo(int len, int * __counted_by(len) p); +@protocol CountedByProtocol + - (void) simple:(int)len :(int * __counted_by(len))p; + - (void) shared:(int)len :(int * __counted_by(len))p1 :(int * __counted_by(len))p2; + - (void) complexExpr:(int)len :(int) offset :(int * __counted_by(len - offset))p; + - (void) nullUnspecified:(int)len :(int * __counted_by(len) _Null_unspecified)p; + - (void) nonnull:(int)len :(int * __counted_by(len) _Nonnull)p; + - (void) nullable:(int)len :(int * __counted_by(len) _Nullable)p; + - (int * __counted_by(len)) returnPointer:(int)len; + + + (void) staticMethod:(int)len :(int * __counted_by(len))p; +@end + +__attribute__((swift_attr("@_SwiftifyImportProtocol(.method(signature: \"func swiftAttr(_ len: Int32, _ p: UnsafeMutablePointer!)\", paramInfo: [.countedBy(pointer: .param(2), count: \"len\")]))"))) +@protocol SwiftAttrProtocol + - (void)swiftAttr:(int)len :(int *)p; +@end + +//--- expansions.expected +@__swiftmacro_So17CountedByProtocol015_SwiftifyImportC0fMe_.swift +------------------------------ +extension CountedByProtocol { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func simple(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe simple(len, p.baseAddress!) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func shared(_ p1: UnsafeMutableBufferPointer, _ p2: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p1.count)! + if p2.count != len { + fatalError("bounds check failure in shared: expected \(len) but got \(p2.count)") + } + return unsafe shared(len, p1.baseAddress!, p2.baseAddress!) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func complexExpr(_ len: Int32, _ offset: Int32, _ p: UnsafeMutableBufferPointer) { + let _pCount = p.count + if _pCount != len - offset { + fatalError("bounds check failure in complexExpr: expected \(len - offset) but got \(_pCount)") + } + return unsafe complexExpr(len, offset, p.baseAddress!) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func nullUnspecified(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe nullUnspecified(len, p.baseAddress!) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func nonnull(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe nonnull(len, p.baseAddress!) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func nullable(_ p: UnsafeMutableBufferPointer?) { + let len = Int32(exactly: unsafe p?.count ?? 0)! + return unsafe nullable(len, p?.baseAddress) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func returnPointer(_ len: Int32) -> UnsafeMutableBufferPointer { + return unsafe UnsafeMutableBufferPointer(start: unsafe returnPointer(len), count: Int(len)) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload + static public func staticMethod(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe staticMethod(len, p.baseAddress!) + } +} +------------------------------ +@__swiftmacro_So17SwiftAttrProtocol015_SwiftifyImportC0fMe_.swift +------------------------------ +extension SwiftAttrProtocol { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func swiftAttr(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe swiftAttr(len, p.baseAddress!) + } +} +------------------------------ +@__swiftmacro_So3foo15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public func foo(_ p: UnsafeMutableBufferPointer) -> Int32 { + let len = Int32(exactly: p.count)! + return unsafe foo(len, p.baseAddress!) +} +------------------------------ +//--- counted-by-protocol.swift +import CountedByProtocolClang + +@inlinable +public func call(p: UnsafeMutableBufferPointer, x: CInt, y: CInt, a: CountedByProtocol, b: SwiftAttrProtocol) { + a.simple(p) + a.shared(p, p) + a.complexExpr(x, y, p) + a.nullUnspecified(p) + a.nonnull(p) + a.nullable(p) + let _: UnsafeMutableBufferPointer = a.returnPointer(x) + let r2 = a.returnPointer(x) + let _: UnsafeMutablePointer? = r2 // make sure the original is the favored overload + b.swiftAttr(p) + let _ = foo(p) +} diff --git a/test/Interop/ObjC/swiftify-import/negative.swift b/test/Interop/ObjC/swiftify-import/negative.swift new file mode 100644 index 0000000000000..2ab4396a6ac5a --- /dev/null +++ b/test/Interop/ObjC/swiftify-import/negative.swift @@ -0,0 +1,97 @@ +// REQUIRES: swift_feature_SafeInteropWrappers + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers %t/test.swift -verify -Xcc -Wno-nullability-completeness +// RUN: env SWIFT_BACKTRACE="" %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers %t/test.swift -dump-macro-expansions 2> %t/expansions.out +// RUN: %diff %t/expansions.out %t/expansions.expected + +//--- test.h +#pragma once + +#define __counted_by(x) __attribute__((__counted_by__(x))) + +@protocol NoBounds + - (void) ignore; +@end + +@protocol NoYesNo + - (void) ignore; + - (void) simple:(int)len :(int * __counted_by(len))p; + - (void) ignore2; +@end + +@protocol YesNo + - (void) simple:(int)len :(int * __counted_by(len))p; + - (void) ignore; +@end + +@protocol YesNoYes + - (void) simple:(int)len :(int * __counted_by(len))p; + - (void) ignore; + - (void) simple2:(int)len :(int * __counted_by(len))p; +@end + +//--- expansions.expected +@__swiftmacro_So05NoYesA023_SwiftifyImportProtocolfMe_.swift +------------------------------ +extension NoYesNo { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func simple(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe simple(len, p.baseAddress!) + } +} +------------------------------ +@__swiftmacro_So5YesNo23_SwiftifyImportProtocolfMe_.swift +------------------------------ +extension YesNo { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func simple(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe simple(len, p.baseAddress!) + } +} +------------------------------ +@__swiftmacro_So05YesNoA023_SwiftifyImportProtocolfMe_.swift +------------------------------ +extension YesNoYes { + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func simple(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe simple(len, p.baseAddress!) + } + /// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload public + func simple2(_ p: UnsafeMutableBufferPointer) { + let len = Int32(exactly: p.count)! + return unsafe simple2(len, p.baseAddress!) + } +} +------------------------------ +//--- test.swift +import TestClang + +@inlinable +public func call(p: UnsafeMutableBufferPointer, x: NoBounds, y: NoYesNo, z: YesNo, å: YesNoYes) { + x.ignore() + y.ignore() + y.simple(p) + z.ignore() + z.simple(p) + å.ignore() + å.simple(p) + å.simple2(p) +} + +//--- module.modulemap +module TestClang { + header "test.h" + export * +} + + diff --git a/test/Macros/SwiftifyImport/CountedBy/Protocol.swift b/test/Macros/SwiftifyImport/CountedBy/Protocol.swift index 38729d224b9d9..3623caa797a71 100644 --- a/test/Macros/SwiftifyImport/CountedBy/Protocol.swift +++ b/test/Macros/SwiftifyImport/CountedBy/Protocol.swift @@ -42,7 +42,7 @@ protocol OverloadedProtocol { ------------------------------ extension SimpleProtocol { /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_disfavoredOverload +@_alwaysEmitIntoClient @_disfavoredOverload public func myFunc(_ ptr: UnsafeBufferPointer) { let len = CInt(exactly: ptr.count)! return unsafe myFunc(ptr.baseAddress!, len) @@ -53,7 +53,7 @@ extension SimpleProtocol { ------------------------------ extension SpanProtocol { /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_disfavoredOverload +@_alwaysEmitIntoClient @_disfavoredOverload public func foo(_ ptr: Span) { let len = CInt(exactly: ptr.count)! return unsafe ptr.withUnsafeBufferPointer { _ptrPtr in @@ -61,7 +61,7 @@ extension SpanProtocol { } } /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_lifetime(borrow self) @_disfavoredOverload +@_alwaysEmitIntoClient @_lifetime(borrow self) @_disfavoredOverload public func bar(_ len: CInt) -> Span { return unsafe _swiftifyOverrideLifetime(Span(_unsafeStart: unsafe bar(len), count: Int(len)), copying: ()) } @@ -71,7 +71,7 @@ extension SpanProtocol { ------------------------------ extension MixedProtocol { /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_disfavoredOverload +@_alwaysEmitIntoClient @_disfavoredOverload public /// Some doc comment func foo(_ ptr: Span) { let len = CInt(exactly: ptr.count)! @@ -80,7 +80,7 @@ extension MixedProtocol { } } /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_disfavoredOverload +@_alwaysEmitIntoClient @_disfavoredOverload public func bar(_ ptr: UnsafeBufferPointer) { let len = CInt(exactly: ptr.count)! return unsafe bar(ptr.baseAddress!, len) @@ -91,13 +91,13 @@ extension MixedProtocol { ------------------------------ extension OverloadedProtocol { /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_disfavoredOverload +@_alwaysEmitIntoClient @_disfavoredOverload public func foo(_ ptr: UnsafeBufferPointer) { let len1 = CInt(exactly: ptr.count)! return unsafe foo(ptr.baseAddress!, len1) } /// This is an auto-generated wrapper for safer interop -@_alwaysEmitIntoClient @_disfavoredOverload +@_alwaysEmitIntoClient @_disfavoredOverload public func foo(bar: UnsafeBufferPointer) { let len2 = CInt(exactly: bar.count)! return unsafe foo(bar: bar.baseAddress!, len2)