From f622a90d30df92c4861677925a047eed1357c48e Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Thu, 5 Dec 2024 14:52:32 -0800 Subject: [PATCH] Infer @PointerBounds macro from clang __counted_by parameters This results in an automatic wrapper function with safe pointer types when the imported function has bounds attributes. This exercises similar pathways as the recently added functionality for specifying macros from swift_attr. The new functionality is guarded by the experimental language feature SafeInteropWrappers. rdar://97942270 --- include/swift/Basic/Features.def | 4 + include/swift/Option/Options.td | 5 + lib/AST/FeatureSet.cpp | 1 + lib/ClangImporter/ClangImporter.cpp | 3 + lib/ClangImporter/ImportDecl.cpp | 194 ++++++++++++------ lib/ClangImporter/ImportType.cpp | 80 ++++---- lib/ClangImporter/ImporterImpl.h | 21 +- .../C/pointer-bounds/Inputs/counted-by.h | 17 ++ .../C/pointer-bounds/Inputs/module.modulemap | 5 + .../Interop/C/pointer-bounds/counted-by.swift | 47 +++++ 10 files changed, 276 insertions(+), 101 deletions(-) create mode 100644 test/Interop/C/pointer-bounds/Inputs/counted-by.h create mode 100644 test/Interop/C/pointer-bounds/Inputs/module.modulemap create mode 100644 test/Interop/C/pointer-bounds/counted-by.swift diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index e33d83ab8313c..f875db31e7174 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -406,6 +406,10 @@ EXPERIMENTAL_FEATURE(WarnUnsafe, true) /// Import unsafe C and C++ constructs as @unsafe. EXPERIMENTAL_FEATURE(SafeInterop, true) +// Import bounds safety and lifetime attributes from interop headers to +// generate Swift wrappers with safe pointer types. +EXPERIMENTAL_FEATURE(SafeInteropWrappers, false) + /// Ignore resilience errors due to C++ types. EXPERIMENTAL_FEATURE(AssumeResilientCxxTypes, true) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index f571be519abd1..d4978c1e26d77 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -792,6 +792,11 @@ def enable_experimental_concise_pound_file : Flag<["-"], Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Enable experimental concise '#file' identifier">; +def enable_experimental_bounds_safety_interop : + Flag<["-"], "enable-experimental-bounds-safety-interop">, + Flags<[NoDriverOption, FrontendOption, HelpHidden, ModuleInterfaceOption]>, + HelpText<"Enable experimental C bounds safety interop code generation and config directives">; + def enable_experimental_cxx_interop : Flag<["-"], "enable-experimental-cxx-interop">, Flags<[NoDriverOption, FrontendOption, HelpHidden, ModuleInterfaceOption]>, diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 4d2381a914779..1f12bd29ec2ef 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -321,6 +321,7 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) { UNINTERESTING_FEATURE(WarnUnsafe) UNINTERESTING_FEATURE(SafeInterop) +UNINTERESTING_FEATURE(SafeInteropWrappers) UNINTERESTING_FEATURE(AssumeResilientCxxTypes) UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError) UNINTERESTING_FEATURE(CoroutineAccessorsAllocateInCallee) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index fd17bbbff7676..eb0fc7b0671f3 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -578,6 +578,9 @@ void importer::getNormalInvocationArguments( } } + if (LangOpts.hasFeature(Feature::SafeInteropWrappers)) + invocationArgStrs.push_back("-fexperimental-bounds-safety-attributes"); + // Set C language options. if (triple.isOSDarwin()) { invocationArgStrs.insert(invocationArgStrs.end(), { diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index e7314128879cb..612f123ef67bd 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -108,7 +108,8 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc, std::optional accessorInfo, DeclName name, SourceLoc nameLoc, GenericParamList *genericParams, ParameterList *bodyParams, Type resultTy, bool async, - bool throws, DeclContext *dc, ClangNode clangNode) { + bool throws, DeclContext *dc, ClangNode clangNode, + bool hasBoundsAnnotation) { FuncDecl *decl; if (accessorInfo) { decl = AccessorDecl::create( @@ -124,6 +125,9 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc, genericParams, dc, clangNode); } impl.importSwiftAttrAttributes(decl); + if (hasBoundsAnnotation) + impl.importBoundsAttributes(decl); + return decl; } @@ -3272,7 +3276,8 @@ namespace { } return Impl.importFunctionParameterList( dc, decl, nonSelfParams, decl->isVariadic(), allowNSUIntegerAsInt, - argNames, genericParams, /*resultType=*/nullptr); + argNames, genericParams, /*resultType=*/nullptr, + /*hasBoundsAnnotatedParam=*/nullptr); } Decl * @@ -3690,6 +3695,7 @@ namespace { bool importFuncWithoutSignature = isa(decl) && Impl.importSymbolicCXXDecls; + bool hasBoundsAnnotation = false; if (!dc->isModuleScopeContext() && !isa(decl)) { // Handle initializers. if (name.getBaseName().isConstructor()) { @@ -3786,7 +3792,7 @@ namespace { importedType = Impl.importFunctionParamsAndReturnType( dc, decl, {decl->param_begin(), decl->param_size()}, decl->isVariadic(), isInSystemModule(dc), name, bodyParams, - templateParams); + templateParams, &hasBoundsAnnotation); } if (auto *mdecl = dyn_cast(decl)) { @@ -3853,10 +3859,10 @@ namespace { auto resultTy = importedType.getType(); FuncDecl *func = - createFuncOrAccessor(Impl, loc, accessorInfo, name, - nameLoc, genericParams, bodyParams, resultTy, + createFuncOrAccessor(Impl, loc, accessorInfo, name, nameLoc, + genericParams, bodyParams, resultTy, /*async=*/false, /*throws=*/false, dc, - clangNode); + clangNode, hasBoundsAnnotation); result = func; if (!dc->isModuleScopeContext()) { @@ -4899,12 +4905,14 @@ namespace { } } - auto result = createFuncOrAccessor(Impl, - /*funcLoc*/ SourceLoc(), accessorInfo, - importedName.getDeclName(), - /*nameLoc*/ SourceLoc(), - /*genericParams=*/nullptr, bodyParams, - resultTy, async, throws, dc, decl); + bool hasBoundsAnnotation = + false; // currently only implemented for functions + auto result = createFuncOrAccessor( + Impl, + /*funcLoc*/ SourceLoc(), accessorInfo, importedName.getDeclName(), + /*nameLoc*/ SourceLoc(), + /*genericParams=*/nullptr, bodyParams, resultTy, async, throws, dc, + decl, hasBoundsAnnotation); result->setAccess(decl->isDirectMethod() ? AccessLevel::Public : getOverridableAccessLevel(dc)); @@ -6544,7 +6552,8 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer( } else { parameterList = Impl.importFunctionParameterList( dc, decl, {decl->param_begin(), decl->param_end()}, decl->isVariadic(), - allowNSUIntegerAsInt, argNames, /*genericParams=*/{}, /*resultType=*/nullptr); + allowNSUIntegerAsInt, argNames, /*genericParams=*/{}, + /*resultType=*/nullptr, /*hasBoundsAnnotatedParam=*/nullptr); } if (!parameterList) return nullptr; @@ -8344,6 +8353,53 @@ static bool importAsUnsafe(ClangImporter::Implementation &impl, return false; } +void ClangImporter::Implementation::importNontrivialAttribute( + Decl *MappedDecl, llvm::StringRef AttrString) { + bool cached = true; + while (true) { + // Dig out a source file we can use for parsing. + auto &sourceFile = getClangSwiftAttrSourceFile( + *MappedDecl->getDeclContext()->getParentModule(), AttrString, cached); + + auto topLevelDecls = sourceFile.getTopLevelDecls(); + + // If we're using the cached version, check whether we can correctly + // clone the attribute. + if (cached) { + bool hasNonclonableAttribute = false; + for (auto decl : topLevelDecls) { + if (hasNonclonableAttribute) + break; + + for (auto attr : decl->getAttrs()) { + if (!attr->canClone()) { + hasNonclonableAttribute = true; + break; + } + } + } + + // We cannot clone one of the attributes. Go back and build a new + // source file without caching it. + if (hasNonclonableAttribute) { + cached = false; + continue; + } + } + + // Collect the attributes from the synthesized top-level declaration in + // the source file. If we're using a cached copy, clone the attribute. + for (auto decl : topLevelDecls) { + SmallVector attrs(decl->getAttrs().begin(), + decl->getAttrs().end()); + for (auto attr : attrs) { + MappedDecl->getAttrs().add(cached ? attr->clone(SwiftContext) : attr); + } + } + break; + } +} + void ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { auto ClangDecl = @@ -8481,53 +8537,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { continue; } - bool cached = true; - while (true) { - // Dig out a source file we can use for parsing. - auto &sourceFile = getClangSwiftAttrSourceFile( - *MappedDecl->getDeclContext()->getParentModule(), - swiftAttr->getAttribute(), - cached); - - auto topLevelDecls = sourceFile.getTopLevelDecls(); - - // If we're using the cached version, check whether we can correctly - // clone the attribute. - if (cached) { - bool hasNonclonableAttribute = false; - for (auto decl : topLevelDecls) { - if (hasNonclonableAttribute) - break; - - for (auto attr : decl->getAttrs()) { - if (!attr->canClone()) { - hasNonclonableAttribute = true; - break; - } - } - } - - // We cannot clone one of the attributes. Go back and build a new - // source file without caching it. - if (hasNonclonableAttribute) { - cached = false; - continue; - } - } - - // Collect the attributes from the synthesized top-level declaration in - // the source file. If we're using a cached copy, clone the attribute. - for (auto decl : topLevelDecls) { - SmallVector attrs(decl->getAttrs().begin(), - decl->getAttrs().end()); - for (auto attr : attrs) { - MappedDecl->getAttrs().add(cached ? attr->clone(SwiftContext) - : attr); - } - } - - break; - } + importNontrivialAttribute(MappedDecl, swiftAttr->getAttribute()); } if (seenUnsafe || importAsUnsafe(*this, ClangDecl, MappedDecl)) { @@ -8613,6 +8623,70 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { } } +namespace { +class PointerParamInfoPrinter { +public: + clang::ASTContext &ctx; + llvm::raw_ostream &out; + bool firstParam = true; + PointerParamInfoPrinter(clang::ASTContext &ctx, llvm::raw_ostream &out) + : ctx(ctx), out(out) { + out << "@PointerBounds("; + } + ~PointerParamInfoPrinter() { out << ")"; } + + void printCountedBy(const clang::CountAttributedType *CAT, + size_t pointerIndex) { + if (!firstParam) { + out << ", "; + } else { + firstParam = false; + } + clang::Expr *countExpr = CAT->getCountExpr(); + bool isSizedBy = CAT->isCountInBytes(); + out << "."; + if (isSizedBy) + out << "sizedBy"; + else + out << "countedBy"; + out << "(pointer: " << pointerIndex + 1 << ", "; + if (isSizedBy) + out << "size"; + else + out << "count"; + out << ": \""; + countExpr->printPretty( + out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr + out << "\")"; + } +}; +} // namespace + +void ClangImporter::Implementation::importBoundsAttributes( + FuncDecl *MappedDecl) { + assert(SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers)); + auto ClangDecl = + dyn_cast_or_null(MappedDecl->getClangDecl()); + // any function with safe pointer imports should have a clang decl + assert(ClangDecl); + if (!ClangDecl) + return; + + llvm::SmallString<128> MacroString; + { + llvm::raw_svector_ostream out(MacroString); + + PointerParamInfoPrinter printer(getClangASTContext(), out); + for (auto [index, param] : llvm::enumerate(ClangDecl->parameters())) { + if (auto CAT = param->getType()->getAs()) { + printer.printCountedBy(CAT, index); + } + } + } + + importNontrivialAttribute(MappedDecl, MacroString); +} + static bool isUsingMacroName(clang::SourceManager &SM, clang::SourceLocation loc, StringRef MacroName) { diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 074e02ff2dba8..4796757a03a41 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -223,6 +223,7 @@ namespace { Bridgeability Bridging; const clang::FunctionType *CompletionHandlerType; std::optional CompletionHandlerErrorParamIndex; + bool isBoundsAnnotated = false; public: SwiftTypeConverter(ClangImporter::Implementation &impl, @@ -245,6 +246,8 @@ namespace { return IR; } + bool hasBoundsAnnotation() { return isBoundsAnnotated; } + ImportResult VisitType(const Type*) = delete; // TODO(https://github.com/apple/swift/issues/56206): Add support for dependent types. @@ -419,13 +422,8 @@ namespace { ImportResult VisitCountAttributedType( const clang::CountAttributedType *type) { - // CountAttributedType is a clang type representing a pointer with - // a "counted_by" type attribute. For now, we don't import these - // into Swift. - // In the future we could do something more clever (such as trying to - // import as an Array where possible) or less clever (such as importing - // as the desugared, underlying pointer type). - return Type(); + isBoundsAnnotated = true; + return Visit(type->desugar()); } ImportResult VisitDynamicRangePointerType( @@ -493,7 +491,9 @@ namespace { // without special hints. Type pointeeType = Impl.importTypeIgnoreIUO( pointeeQualType, ImportTypeKind::Value, addImportDiagnostic, - AllowNSUIntegerAsInt, Bridgeability::None, ImportTypeAttrs()); + AllowNSUIntegerAsInt, Bridgeability::None, ImportTypeAttrs(), + OTK_ImplicitlyUnwrappedOptional, /*resugarNSErrorPointer=*/true, + &isBoundsAnnotated); // If this is imported as a reference type, ignore the innermost pointer. // (`T *` becomes `T`, but `T **` becomes `UnsafeMutablePointer`.) @@ -1722,7 +1722,8 @@ ImportedType ClangImporter::Implementation::importType( llvm::function_ref addImportDiagnosticFn, bool allowNSUIntegerAsInt, Bridgeability bridging, ImportTypeAttrs attrs, OptionalTypeKind optionality, bool resugarNSErrorPointer, - std::optional completionHandlerErrorParamIndex) { + std::optional completionHandlerErrorParamIndex, + bool *isBoundsAnnotated) { if (type.isNull()) return {Type(), false}; @@ -1784,6 +1785,8 @@ ImportedType ClangImporter::Implementation::importType( *this, addImportDiagnosticFn, allowNSUIntegerAsInt, bridging, completionHandlerType, completionHandlerErrorParamIndex); auto importResult = converter.Visit(type); + if (isBoundsAnnotated) + *isBoundsAnnotated |= converter.hasBoundsAnnotation(); // Now fix up the type based on how we're concretely using it. auto adjustedType = adjustTypeForConcreteImport( @@ -1797,13 +1800,14 @@ ImportedType ClangImporter::Implementation::importType( Type ClangImporter::Implementation::importTypeIgnoreIUO( clang::QualType type, ImportTypeKind importKind, llvm::function_ref addImportDiagnosticFn, - bool allowNSUIntegerAsInt, Bridgeability bridging, - ImportTypeAttrs attrs, OptionalTypeKind optionality, - bool resugarNSErrorPointer) { + bool allowNSUIntegerAsInt, Bridgeability bridging, ImportTypeAttrs attrs, + OptionalTypeKind optionality, bool resugarNSErrorPointer, + bool *isBoundsAnnotated) { - auto importedType = importType(type, importKind, addImportDiagnosticFn, - allowNSUIntegerAsInt, bridging, attrs, - optionality, resugarNSErrorPointer); + auto importedType = + importType(type, importKind, addImportDiagnosticFn, allowNSUIntegerAsInt, + bridging, attrs, optionality, resugarNSErrorPointer, + std::nullopt, isBoundsAnnotated); return importedType.getType(); } @@ -2188,7 +2192,7 @@ applyImportTypeAttrs(ImportTypeAttrs attrs, Type type, ImportedType ClangImporter::Implementation::importFunctionReturnType( DeclContext *dc, const clang::FunctionDecl *clangDecl, - bool allowNSUIntegerAsInt) { + bool allowNSUIntegerAsInt, bool *isBoundsAnnotated) { // Hardcode handling of certain result types for builtins. if (auto builtinID = clangDecl->getBuiltinID()) { @@ -2297,13 +2301,13 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( } // Import the result type. - return importType(returnType, - (isAuditedResult ? ImportTypeKind::AuditedResult - : ImportTypeKind::Result), - ImportDiagnosticAdder(*this, clangDecl, - clangDecl->getLocation()), - allowNSUIntegerAsInt, Bridgeability::Full, - getImportTypeAttrs(clangDecl), OptionalityOfReturn); + return importType( + returnType, + (isAuditedResult ? ImportTypeKind::AuditedResult + : ImportTypeKind::Result), + ImportDiagnosticAdder(*this, clangDecl, clangDecl->getLocation()), + allowNSUIntegerAsInt, Bridgeability::Full, getImportTypeAttrs(clangDecl), + OptionalityOfReturn, isBoundsAnnotated); } static Type @@ -2338,7 +2342,7 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, bool isFromSystemModule, DeclName name, ParameterList *¶meterList, - ArrayRef genericParams) { + ArrayRef genericParams, bool *hasBoundsAnnotation) { bool allowNSUIntegerAsInt = shouldAllowNSUIntegerAsInt(isFromSystemModule, clangDecl); @@ -2395,8 +2399,8 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( // If importedType is already initialized, it means we found the enum that // was supposed to be used (instead of the typedef type). if (!importedType) { - importedType = - importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); + importedType = importFunctionReturnType( + dc, clangDecl, allowNSUIntegerAsInt, hasBoundsAnnotation); if (!importedType) { addDiag(Diagnostic(diag::return_type_not_imported)); return {Type(), false}; @@ -2406,9 +2410,9 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( Type swiftResultTy = importedType.getType(); ArrayRef argNames = name.getArgumentNames(); - parameterList = importFunctionParameterList(dc, clangDecl, params, isVariadic, - allowNSUIntegerAsInt, argNames, - genericParams, swiftResultTy); + parameterList = importFunctionParameterList( + dc, clangDecl, params, isVariadic, allowNSUIntegerAsInt, argNames, + genericParams, swiftResultTy, hasBoundsAnnotation); if (!parameterList) return {Type(), false}; @@ -2545,6 +2549,7 @@ ClangImporter::Implementation::importParameterType( } } + bool isBoundsAnnotated = false; if (!swiftParamTy) { // If this is the throws error parameter, we don't need to convert any // NSError** arguments to the sugared NSErrorPointer typealias form, @@ -2554,11 +2559,11 @@ ClangImporter::Implementation::importParameterType( // for the specific case when the throws conversion works, but is not // sufficient if it fails. (The correct, overarching fix is ClangImporter // being lazier.) - auto importedType = importType(paramTy, importKind, addImportDiagnosticFn, - allowNSUIntegerAsInt, Bridgeability::Full, - attrs, optionalityOfParam, - /*resugarNSErrorPointer=*/!paramIsError, - completionHandlerErrorParamIndex); + auto importedType = importType( + paramTy, importKind, addImportDiagnosticFn, allowNSUIntegerAsInt, + Bridgeability::Full, attrs, optionalityOfParam, + /*resugarNSErrorPointer=*/!paramIsError, + completionHandlerErrorParamIndex, &isBoundsAnnotated); if (!importedType) return std::nullopt; @@ -2576,7 +2581,7 @@ ClangImporter::Implementation::importParameterType( isInOut = false; return ImportParameterTypeResult{swiftParamTy, isInOut, isConsuming, - isParamTypeImplicitlyUnwrapped}; + isParamTypeImplicitlyUnwrapped, isBoundsAnnotated}; } bool ClangImporter::Implementation::isDefaultArgSafeToImport( @@ -2689,7 +2694,8 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, bool allowNSUIntegerAsInt, ArrayRef argNames, - ArrayRef genericParams, Type resultType) { + ArrayRef genericParams, Type resultType, + bool *hasBoundsAnnotatedParam) { // Import the parameters. SmallVector parameters; unsigned index = 0; @@ -2730,6 +2736,8 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( bool isConsuming = swiftParamTyOpt->isConsuming; bool isParamTypeImplicitlyUnwrapped = swiftParamTyOpt->isParamTypeImplicitlyUnwrapped; + if (swiftParamTyOpt->isBoundsAnnotated && hasBoundsAnnotatedParam) + *hasBoundsAnnotatedParam = true; // Retrieve the argument name. Identifier name; diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 1812911b224dc..5ac9a4b2d5260 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1060,6 +1060,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation SourceFile &getClangSwiftAttrSourceFile( ModuleDecl &module, StringRef attributeText, bool cached); + /// Create attribute with given text and attach it to decl, creating or + /// retrieving a chached source file as needed. + void importNontrivialAttribute(Decl *MappedDecl, StringRef attributeText); + /// Utility function to import Clang attributes from a source Swift decl to /// synthesized Swift decl. /// @@ -1358,7 +1362,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation ImportTypeAttrs attrs, OptionalTypeKind optional = OTK_ImplicitlyUnwrappedOptional, bool resugarNSErrorPointer = true, - std::optional completionHandlerErrorParamIndex = std::nullopt); + std::optional completionHandlerErrorParamIndex = std::nullopt, + bool *isBoundsAnnotated = nullptr); /// Import the given Clang type into Swift. /// @@ -1375,7 +1380,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool allowNSUIntegerAsInt, Bridgeability topLevelBridgeability, ImportTypeAttrs attrs, OptionalTypeKind optional = OTK_ImplicitlyUnwrappedOptional, - bool resugarNSErrorPointer = true); + bool resugarNSErrorPointer = true, bool *isBoundsAnnotated = nullptr); /// Import the given Clang type into Swift, returning the /// Swift parameters and result type and whether we should treat it @@ -1408,7 +1413,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, bool isFromSystemModule, DeclName name, ParameterList *¶meterList, - ArrayRef genericParams); + ArrayRef genericParams, + bool *hasBoundsAnnotatedParam); /// Import the given function return type. /// @@ -1422,7 +1428,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// imported. ImportedType importFunctionReturnType(DeclContext *dc, const clang::FunctionDecl *clangDecl, - bool allowNSUIntegerAsInt); + bool allowNSUIntegerAsInt, + bool *isBoundsAnnotated = nullptr); /// Import the parameter list for a function /// @@ -1439,7 +1446,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, bool allowNSUIntegerAsInt, ArrayRef argNames, - ArrayRef genericParams, Type resultType); + ArrayRef genericParams, Type resultType, + bool *hasBoundsAnnotatedParam); struct ImportParameterTypeResult { /// The imported parameter Swift type. @@ -1450,6 +1458,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool isConsuming; /// If the parameter is implicitly unwrapped or not. bool isParamTypeImplicitlyUnwrapped; + /// If the parameter has (potentially nested) safe pointer types + bool isBoundsAnnotated; }; /// Import a parameter type @@ -1735,6 +1745,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation } void importSwiftAttrAttributes(Decl *decl); + void importBoundsAttributes(FuncDecl *MappedDecl); /// Find the lookup table that corresponds to the given Clang module. /// diff --git a/test/Interop/C/pointer-bounds/Inputs/counted-by.h b/test/Interop/C/pointer-bounds/Inputs/counted-by.h new file mode 100644 index 0000000000000..0e27c038b0a31 --- /dev/null +++ b/test/Interop/C/pointer-bounds/Inputs/counted-by.h @@ -0,0 +1,17 @@ +#pragma once + +#define __counted_by(x) __attribute__((__counted_by__(x))) + +void simple(int len, int * __counted_by(len) p); + +void swiftAttr(int len, int * p) __attribute__((swift_attr("@PointerBounds(.countedBy(pointer: 2, count: \"len\"))"))); + +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); diff --git a/test/Interop/C/pointer-bounds/Inputs/module.modulemap b/test/Interop/C/pointer-bounds/Inputs/module.modulemap new file mode 100644 index 0000000000000..c36e691a1036f --- /dev/null +++ b/test/Interop/C/pointer-bounds/Inputs/module.modulemap @@ -0,0 +1,5 @@ +module CountedByClang { + header "counted-by.h" + export * +} + diff --git a/test/Interop/C/pointer-bounds/counted-by.swift b/test/Interop/C/pointer-bounds/counted-by.swift new file mode 100644 index 0000000000000..ba5155a1d32c4 --- /dev/null +++ b/test/Interop/C/pointer-bounds/counted-by.swift @@ -0,0 +1,47 @@ +// REQUIRES: swift_feature_SafeInteropWrappers +// REQUIRES: pointer_bounds + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -disable-availability-checking -plugin-path %swift-plugin-dir -o %t/CountedBy.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers %s + +// Check that ClangImporter correctly infers and expands @PointerBounds macros for functions with __counted_by parameters. + +import CountedByClang + +@inlinable +public func callSimple(_ p: UnsafeMutableBufferPointer) { + simple(p) +} + +// Check that macros from swift_attr work the same as inferred macros. +@inlinable +public func callSwiftAttr(_ p: UnsafeMutableBufferPointer) { + swiftAttr(p) +} + +@inlinable +public func callShared(_ len: CInt, _ p1: UnsafeMutableBufferPointer, _ p2: UnsafeMutableBufferPointer) { + shared(len, p1, p2) +} + +@inlinable +public func callComplexExpr(_ len: CInt, _ offset: CInt, _ p: UnsafeMutableBufferPointer) { + complexExpr(len, offset, p) +} + + +@inlinable +public func callNullUnspecified(_ p: UnsafeMutableBufferPointer) { + nullUnspecified(p) +} + +@inlinable +public func callNonnull(_ p: UnsafeMutableBufferPointer) { + nonnull(p) +} + +@inlinable +public func callNullable(_ p: UnsafeMutableBufferPointer?) { + nullable(p) +} +