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) +} +