diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index f3f6a446cd3e2..88fd32edc5972 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -7887,8 +7887,9 @@ getRefParentDecls(const clang::RecordDecl *decl, ASTContext &ctx, return matchingDecls; } -static llvm::SmallVector -getValueDeclsForName(const clang::Decl *decl, ASTContext &ctx, StringRef name) { +llvm::SmallVector +importer::getValueDeclsForName( + const clang::Decl *decl, ASTContext &ctx, StringRef name) { llvm::SmallVector results; auto *clangMod = decl->getOwningModule(); if (clangMod && clangMod->isSubModule()) @@ -8232,6 +8233,9 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator, auto cxxDecl = dyn_cast(decl); if (!cxxDecl) { + if (hasNonCopyableAttr(decl)) + return CxxRecordSemanticsKind::MoveOnly; + return CxxRecordSemanticsKind::Trivial; } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 5d45b9a419a24..d94f664ea8246 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2687,6 +2687,9 @@ namespace { } } + // If we need it, add an explicit "deinit" to this type. + synthesizer.addExplicitDeinitIfRequired(result, decl); + result->setMemberLoader(&Impl, 0); return result; } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 84ea4abebba77..236dd8aa18918 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -2179,6 +2179,14 @@ inline clang::QualType desugarIfBoundsAttributed(clang::QualType type) { /// If \a type is an elaborated type, it should be desugared first. ImportedType findOptionSetEnum(clang::QualType type, ClangImporter::Implementation &Impl); + +/// Find value declarations in the same module as the given Clang declaration +/// and with the given name. +/// +/// The name we're looking for is the Swift name. +llvm::SmallVector +getValueDeclsForName(const clang::Decl *decl, ASTContext &ctx, StringRef name); + } // end namespace importer } // end namespace swift diff --git a/lib/ClangImporter/SwiftBridging/swift/bridging b/lib/ClangImporter/SwiftBridging/swift/bridging index 804673a27c878..4c8e97d4a8e80 100644 --- a/lib/ClangImporter/SwiftBridging/swift/bridging +++ b/lib/ClangImporter/SwiftBridging/swift/bridging @@ -1,5 +1,5 @@ -// -*- C++ -*- -//===------------------ bridging - C++ and Swift Interop --------*- C++ -*-===// +// -*- C -*- +//===------------------ bridging - C and Swift Interop ----------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -32,7 +32,7 @@ /// that return a `class` or `struct` type that is annotated with this macro. #define SWIFT_SELF_CONTAINED __attribute__((swift_attr("import_owned"))) -/// Specifies that a C++ method returns a value that is presumed to contain +/// Specifies that a method returns a value that is presumed to contain /// objects whose lifetime is not dependent on `this` or other parameters passed /// to the method. #define SWIFT_RETURNS_INDEPENDENT_VALUE __attribute__((swift_attr("import_unsafe"))) @@ -45,7 +45,7 @@ #define _CXX_INTEROP_CONCAT(...) \ _CXX_INTEROP_CONCAT_(__VA_ARGS__,,,,,,,,,,,,,,,,,) -/// Specifies that a C++ `class` or `struct` is reference-counted using +/// Specifies that a `class` or `struct` is reference-counted using /// the given `retain` and `release` functions. This annotation lets Swift import /// such a type as reference counted type in Swift, taking advantage of Swift's /// automatic reference counting. @@ -76,7 +76,7 @@ __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \ __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:_release)))) -/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime +/// Specifies that a `class` or `struct` is a reference type whose lifetime /// is presumed to be immortal, i.e. the reference to such object is presumed to /// always be valid. This annotation lets Swift import such a type as a reference /// type in Swift. @@ -103,7 +103,7 @@ __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \ __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal)))) -/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime +/// Specifies that a `class` or `struct` is a reference type whose lifetime /// is not managed automatically. The programmer must validate that any reference /// to such object is valid themselves. This annotation lets Swift import such a type as a reference type in Swift. #define SWIFT_UNSAFE_REFERENCE \ @@ -115,7 +115,7 @@ /// Specifies a name that will be used in Swift for this declaration instead of its original name. #define SWIFT_NAME(_name) __attribute__((swift_name(#_name))) -/// Specifies that a specific C++ `class` or `struct` conforms to a +/// Specifies that a specific `class` or `struct` conforms to a /// a specific Swift protocol. /// /// This example shows how to use this macro to conform a class template to a Swift protocol: @@ -147,7 +147,7 @@ #define SWIFT_MUTATING \ __attribute__((swift_attr("mutating"))) -/// Specifies that a specific c++ type such class or struct should be imported as type marked +/// Specifies that a specific class or struct should be imported as type marked /// as `@unchecked Sendable` type in swift. If this annotation is used, the type is therefore allowed to /// use safely across async contexts. /// @@ -160,19 +160,43 @@ #define SWIFT_UNCHECKED_SENDABLE \ __attribute__((swift_attr("@Sendable"))) -/// Specifies that a specific c++ type such class or struct should be imported -/// as a non-copyable Swift value type. +/// Specifies that a `class` or `struct` should be imported as a non-copyable +/// Swift value type. #define SWIFT_NONCOPYABLE \ __attribute__((swift_attr("~Copyable"))) -/// Specifies that a specific c++ type such class or struct should be imported -/// as a non-escapable Swift value type when the non-escapable language feature -/// is enabled. +/// Specifies that a `class` or `struct` should be imported as a non-copyable +/// Swift value type that calls the given `_destroy` function when a value is no +/// longer used. +/// +/// This example shows how to use this macro to let Swift know that +/// a given C struct should have its members freed when it goes out of scope: +/// ```c +/// typedef struct SWIFT_NONCOPYABLE_WITH_DESTROY(mytypeFreeMembers) MyType { +/// void *storage +/// } MyType; +/// +/// void mytypeFreeMembers(MyType toBeDestroyed); +/// MyType mytypeCreate(void); +/// ``` +/// +/// Usage in Swift: +/// ```swift +/// let mt = mytypeCreate() +/// let mt2 = mt // consumes mt +/// // once mt2 is unused, Swift will call mytypeFreeMembers(mt2) +/// ``` +#define SWIFT_NONCOPYABLE_WITH_DESTROY(_destroy) \ + __attribute__((swift_attr("~Copyable"))) \ + __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(destroy:_destroy)))) + +/// Specifies that a specific class or struct should be imported +/// as a non-escapable Swift value type. #define SWIFT_NONESCAPABLE \ __attribute__((swift_attr("~Escapable"))) -/// Specifies that a specific c++ type such class or struct should be imported -/// as a escapable Swift value. While this matches the default behavior, +/// Specifies that a specific class or struct should be imported +/// as an escapable Swift value. While this matches the default behavior, /// in safe mode interop mode it ensures that the type is not marked as /// unsafe. #define SWIFT_ESCAPABLE \ @@ -183,16 +207,16 @@ #define SWIFT_ESCAPABLE_IF(...) \ __attribute__((swift_attr("escapable_if:" _CXX_INTEROP_CONCAT(__VA_ARGS__)))) -/// Specifies that the return value is passed as owned for C++ functions and +/// Specifies that the return value is passed as owned for functions and /// methods returning types annotated as `SWIFT_SHARED_REFERENCE` #define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained"))) -/// Specifies that the return value is passed as unowned for C++ functions and +/// Specifies that the return value is passed as unowned for functions and /// methods returning types annotated as `SWIFT_SHARED_REFERENCE` #define SWIFT_RETURNS_UNRETAINED \ __attribute__((swift_attr("returns_unretained"))) -/// Applied to a C++ foreign reference type annotated with -/// SWIFT_SHARED_REFERENCE. Indicates that C++ APIs returning this type are +/// Applied to a foreign reference type annotated with +/// SWIFT_SHARED_REFERENCE. Indicates that APIs returning this type are /// assumed to return an unowned (+0) value by default, unless explicitly annotated /// with SWIFT_RETURNS_RETAINED. /// @@ -203,7 +227,7 @@ /// Bar { ... }; /// ``` /// -/// In Swift, C++ APIs returning `Bar*` will be assumed to return an unowned +/// In Swift, APIs returning `Bar*` will be assumed to return an unowned /// value. #define SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT \ __attribute__((swift_attr("returned_as_unretained_by_default"))) @@ -258,6 +282,7 @@ #define SWIFT_MUTATING #define SWIFT_UNCHECKED_SENDABLE #define SWIFT_NONCOPYABLE +#define SWIDT_NONCOPYABLE_WITH_DESTROY(_destroy) #define SWIFT_NONESCAPABLE #define SWIFT_ESCAPABLE #define SWIFT_ESCAPABLE_IF(...) diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 6deac6fcc741e..95dafed1fd7ac 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -23,6 +23,7 @@ #include "swift/AST/Stmt.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Assertions.h" +#include "swift/ClangImporter/ClangImporterRequests.h" #include "clang/AST/Mangle.h" #include "clang/Sema/DelayedDiagnostic.h" @@ -2809,6 +2810,179 @@ synthesizeAvailabilityDomainPredicateBody(AbstractFunctionDecl *afd, return {body, /*isTypeChecked=*/false}; } +/// Mark the given declaration as always deprecated for the given reason. +static void markDeprecated(Decl *decl, llvm::Twine message) { + ASTContext &ctx = decl->getASTContext(); + decl->getAttrs().add( + AvailableAttr::createUniversallyDeprecated( + ctx, ctx.AllocateCopy(message.str()))); +} + +/// Find an explicitly-provided "destroy" operation specified for the +/// given Clang type and return it. +FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy( + NominalTypeDecl *nominal, const clang::RecordDecl *clangType) { + if (!clangType->hasAttrs()) + return nullptr; + + llvm::SmallPtrSet matchingDestroyFuncs; + llvm::TinyPtrVector nonMatchingDestroyFuncs; + for (auto attr : clangType->getAttrs()) { + auto swiftAttr = dyn_cast(attr); + if (!swiftAttr) + continue; + + auto destroyFuncName = swiftAttr->getAttribute(); + if (!destroyFuncName.consume_front("destroy:")) + continue; + + auto decls = getValueDeclsForName( + clangType, nominal->getASTContext(), destroyFuncName); + for (auto decl : decls) { + auto func = dyn_cast(decl); + if (!func) + continue; + + auto params = func->getParameters(); + if (params->size() != 1) { + nonMatchingDestroyFuncs.push_back(func); + continue; + } + + if (!params->get(0)->getInterfaceType()->isEqual( + nominal->getDeclaredInterfaceType())) { + nonMatchingDestroyFuncs.push_back(func); + continue; + } + + matchingDestroyFuncs.insert(func); + } + } + + switch (matchingDestroyFuncs.size()) { + case 0: + if (!nonMatchingDestroyFuncs.empty()) { + markDeprecated( + nominal, + "destroy function '" + + nonMatchingDestroyFuncs.front()->getName().getBaseName() + .userFacingName() + + "' must have a single parameter with type '" + + nominal->getDeclaredInterfaceType().getString() + "'"); + } + + return nullptr; + + case 1: + // Handled below. + break; + + default: { + auto iter = matchingDestroyFuncs.begin(); + auto first = *iter++; + auto second = *iter; + markDeprecated( + nominal, + "multiple destroy operations ('" + + first->getName().getBaseName().userFacingName() + + "' and '" + + second->getName().getBaseName().userFacingName() + + "') provided for type"); + return nullptr; + } + } + + auto destroyFunc = *matchingDestroyFuncs.begin(); + + // If this type isn't imported as noncopyable, we can't respect the request + // for a destroy operation. + ASTContext &ctx = ImporterImpl.SwiftContext; + auto semanticsKind = evaluateOrDefault( + ctx.evaluator, + CxxRecordSemantics({clangType, ctx, &ImporterImpl}), {}); + switch (semanticsKind) { + case CxxRecordSemanticsKind::Owned: + case CxxRecordSemanticsKind::Reference: + if (auto cxxRecord = dyn_cast(clangType)) { + if (!cxxRecord->hasTrivialDestructor()) { + markDeprecated( + nominal, + "destroy operation '" + + destroyFunc->getName().getBaseName().userFacingName() + + "' is not allowed on types with a non-trivial destructor"); + return nullptr; + } + } + + LLVM_FALLTHROUGH; + + case CxxRecordSemanticsKind::Trivial: + markDeprecated( + nominal, + "destroy operation '" + + destroyFunc->getName().getBaseName().userFacingName() + + "' is only allowed on non-copyable types; " + "did you mean to use SWIFT_NONCOPYABLE?"); + return nullptr; + + case CxxRecordSemanticsKind::Iterator: + case CxxRecordSemanticsKind::MissingLifetimeOperation: + case CxxRecordSemanticsKind::SwiftClassType: + return nullptr; + + case CxxRecordSemanticsKind::MoveOnly: + case CxxRecordSemanticsKind::UnavailableConstructors: + // Handled below. + break; + } + if (semanticsKind != CxxRecordSemanticsKind::MoveOnly) { + return nullptr; + } + + return destroyFunc; +} + +/// Function body synthesizer for a deinit of a noncopyable type, which +/// passes "self" to the given "destroy" function. +static std::pair +synthesizeDeinitBodyForCustomDestroy( + AbstractFunctionDecl *deinitFunc, void *opaqueDestroyFunc) { + auto deinit = cast(deinitFunc); + auto destroyFunc = static_cast(opaqueDestroyFunc); + + ASTContext &ctx = deinit->getASTContext(); + auto funcRef = new (ctx) DeclRefExpr( + destroyFunc, DeclNameLoc(), /*Implicit=*/true); + auto selfRef = new (ctx) DeclRefExpr( + deinit->getImplicitSelfDecl(), DeclNameLoc(), /*Implicit=*/true); + auto callExpr = CallExpr::createImplicit( + ctx, funcRef, + ArgumentList::createImplicit( + ctx, + { Argument(SourceLoc(), Identifier(), selfRef)} + ) + ); + + auto braceStmt = BraceStmt::createImplicit(ctx, { ASTNode(callExpr) }); + return std::make_pair(braceStmt, /*typechecked=*/false); +} + +void SwiftDeclSynthesizer::addExplicitDeinitIfRequired( + NominalTypeDecl *nominal, const clang::RecordDecl *clangType) { + auto destroyFunc = findExplicitDestroy(nominal, clangType); + if (!destroyFunc) + return; + + ASTContext &ctx = nominal->getASTContext(); + auto destructor = new (ctx) DestructorDecl(SourceLoc(), nominal); + destructor->setSynthesized(true); + destructor->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/true); + destructor->setBodySynthesizer( + synthesizeDeinitBodyForCustomDestroy, destroyFunc); + + nominal->addMember(destructor); +} + FuncDecl *SwiftDeclSynthesizer::makeAvailabilityDomainPredicate( const clang::VarDecl *var) { ASTContext &ctx = ImporterImpl.SwiftContext; diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.h b/lib/ClangImporter/SwiftDeclSynthesizer.h index 8ee1a105b0a82..974e2d5a93be3 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.h +++ b/lib/ClangImporter/SwiftDeclSynthesizer.h @@ -341,6 +341,12 @@ class SwiftDeclSynthesizer { synthesizeStaticFactoryForCXXForeignRef( const clang::CXXRecordDecl *cxxRecordDecl); + /// Look for an explicitly-provided "destroy" operation. If one exists + /// and the type has been imported as noncopyable, add an explicit `deinit` + /// that calls that destroy operation. + void addExplicitDeinitIfRequired( + NominalTypeDecl *nominal, const clang::RecordDecl *clangType); + /// Synthesize a Swift function that calls the Clang runtime predicate /// function for the availability domain represented by `var`. FuncDecl *makeAvailabilityDomainPredicate(const clang::VarDecl *var); @@ -349,6 +355,12 @@ class SwiftDeclSynthesizer { private: Type getConstantLiteralType(Type type, ConstantConvertKind convertKind); + + /// Find an explicitly-provided "destroy" operation specified for the + /// given Clang type and return it. + FuncDecl *findExplicitDestroy( + NominalTypeDecl *nominal, const clang::RecordDecl *clangType); + }; } // namespace swift diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index 1b7f71dd99dc3..90b72aec9de03 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -374,12 +374,14 @@ namespace { unsigned explosionSize, llvm::Type *storageType, Size size, SpareBitVector &&spareBits, Alignment align, + IsTriviallyDestroyable_t isTriviallyDestroyable, + IsCopyable_t isCopyable, const clang::RecordDecl *clangDecl) : StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo, fields, explosionSize, FieldsAreABIAccessible, storageType, size, std::move(spareBits), align, - IsTriviallyDestroyable, - IsCopyable, + isTriviallyDestroyable, + isCopyable, IsFixedSize, IsABIAccessible), ClangDecl(clangDecl) {} @@ -469,6 +471,7 @@ namespace { AddressOnlyPointerAuthRecordTypeInfo(ArrayRef fields, llvm::Type *storageType, Size size, Alignment align, + IsCopyable_t isCopyable, const clang::RecordDecl *clangDecl) : StructTypeInfoBase(StructTypeInfoKind::AddressOnlyClangRecordTypeInfo, fields, FieldsAreABIAccessible, storageType, size, @@ -477,7 +480,7 @@ namespace { SpareBitVector(std::optional{ llvm::APInt(size.getValueInBits(), 0)}), align, IsNotTriviallyDestroyable, - IsNotBitwiseTakable, IsCopyable, IsFixedSize, + IsNotBitwiseTakable, isCopyable, IsFixedSize, IsABIAccessible), clangDecl(clangDecl) { (void)clangDecl; @@ -645,6 +648,7 @@ namespace { AddressOnlyCXXClangRecordTypeInfo(ArrayRef fields, llvm::Type *storageType, Size size, Alignment align, + IsCopyable_t isCopyable, const clang::RecordDecl *clangDecl) : StructTypeInfoBase(StructTypeInfoKind::AddressOnlyClangRecordTypeInfo, fields, FieldsAreABIAccessible, storageType, size, @@ -654,9 +658,7 @@ namespace { llvm::APInt(size.getValueInBits(), 0)}), align, IsNotTriviallyDestroyable, IsNotBitwiseTakable, - // TODO: Set this appropriately for the type's - // C++ import behavior. - IsCopyable, IsFixedSize, IsABIAccessible), + isCopyable, IsFixedSize, IsABIAccessible), ClangDecl(clangDecl) { (void)ClangDecl; } @@ -1342,15 +1344,26 @@ class ClangRecordLowering { llvmType->setBody(LLVMFields, /*packed*/ true); if (SwiftType.getStructOrBoundGenericStruct()->isCxxNonTrivial()) { return AddressOnlyCXXClangRecordTypeInfo::create( - FieldInfos, llvmType, TotalStride, TotalAlignment, ClangDecl); + FieldInfos, llvmType, TotalStride, TotalAlignment, + (SwiftDecl && !SwiftDecl->canBeCopyable()) + ? IsNotCopyable : IsCopyable, + ClangDecl); } if (SwiftType.getStructOrBoundGenericStruct()->isNonTrivialPtrAuth()) { return AddressOnlyPointerAuthRecordTypeInfo::create( - FieldInfos, llvmType, TotalStride, TotalAlignment, ClangDecl); + FieldInfos, llvmType, TotalStride, TotalAlignment, + (SwiftDecl && !SwiftDecl->canBeCopyable()) + ? IsNotCopyable : IsCopyable, + ClangDecl); } return LoadableClangRecordTypeInfo::create( FieldInfos, NextExplosionIndex, llvmType, TotalStride, - std::move(SpareBits), TotalAlignment, ClangDecl); + std::move(SpareBits), TotalAlignment, + (SwiftDecl && SwiftDecl->getValueTypeDestructor()) + ? IsNotTriviallyDestroyable : IsTriviallyDestroyable, + (SwiftDecl && !SwiftDecl->canBeCopyable()) + ? IsNotCopyable : IsCopyable, + ClangDecl); } private: diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 6b75a571c2d89..2e96cce09b59c 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -84,6 +84,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Set of delayed conformances that have already been forced. llvm::DenseSet forcedConformances; + /// Imported noncopyable types that we have seen. + llvm::DenseSet importedNontrivialNoncopyableTypes; + size_t anonymousSymbolCounter = 0; std::optional StringToNSStringFn; @@ -282,6 +285,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { void visitMacroDecl(MacroDecl *d); void visitMacroExpansionDecl(MacroExpansionDecl *d); + void visitImportedNontrivialNoncopyableType(NominalTypeDecl *nominal); + // Same as AbstractStorageDecl::visitEmittedAccessors, but skips over skipped // (unavailable) decls. void visitEmittedAccessors(AbstractStorageDecl *D, diff --git a/lib/SILGen/SILGenLazyConformance.cpp b/lib/SILGen/SILGenLazyConformance.cpp index 90cc4772a9219..31674443b117f 100644 --- a/lib/SILGen/SILGenLazyConformance.cpp +++ b/lib/SILGen/SILGenLazyConformance.cpp @@ -110,6 +110,13 @@ void SILGenModule::useConformancesFromType(CanType type) { if (isa(decl)) return; + // If this is an imported noncopyable type with a deinitializer, record it. + if (decl->hasClangNode() && !decl->canBeCopyable() && + decl->getValueTypeDestructor() && + importedNontrivialNoncopyableTypes.insert(decl).second) { + visitImportedNontrivialNoncopyableType(decl); + } + auto genericSig = decl->getGenericSignature(); if (!genericSig) return; diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index f89955564a145..be1bef8a79098 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1577,6 +1577,13 @@ void SILGenModule::visitNominalTypeDecl(NominalTypeDecl *ntd) { SILGenType(*this, ntd).emitType(); } +void SILGenModule::visitImportedNontrivialNoncopyableType( + NominalTypeDecl *nominal) { + emitNonCopyableTypeDeinitTable(nominal); + SILGenType(*this, nominal) + .visitDestructorDecl(nominal->getValueTypeDestructor()); +} + /// SILGenExtension - an ASTVisitor for generating SIL from method declarations /// and protocol conformances inside type extensions. class SILGenExtension : public TypeMemberVisitor { diff --git a/test/Interop/C/struct/Inputs/StructAsOptionSet.apinotes b/test/Interop/C/struct/Inputs/StructAsOptionSet.apinotes new file mode 100644 index 0000000000000..bf409b50f9945 --- /dev/null +++ b/test/Interop/C/struct/Inputs/StructAsOptionSet.apinotes @@ -0,0 +1,14 @@ +--- +Name: StructAsOptionSet +Typedefs: +- Name: WGPUBufferUsage + SwiftConformsTo: Swift.OptionSet + SwiftWrapper: struct +Globals: +- Name: WGPUBufferUsage_None + SwiftName: WGPUBufferUsage.none +- Name: WGPUBufferUsage_MapRead + SwiftName: WGPUBufferUsage.mapRead +- Name: WGPUBufferUsage_MapWrite + SwiftName: WGPUBufferUsage.mapWrite + diff --git a/test/Interop/C/struct/Inputs/module.modulemap b/test/Interop/C/struct/Inputs/module.modulemap index 641158b9978ef..82698ffd42c87 100644 --- a/test/Interop/C/struct/Inputs/module.modulemap +++ b/test/Interop/C/struct/Inputs/module.modulemap @@ -10,3 +10,7 @@ module ForeignReference { module StructAsOptionSet { header "struct-as-option-set.h" } + +module NoncopyableStructs { + header "noncopyable-struct.h" +} diff --git a/test/Interop/C/struct/Inputs/noncopyable-struct.h b/test/Interop/C/struct/Inputs/noncopyable-struct.h new file mode 100644 index 0000000000000..321a86469e6b4 --- /dev/null +++ b/test/Interop/C/struct/Inputs/noncopyable-struct.h @@ -0,0 +1,51 @@ +#include "swift/bridging" + +typedef struct __attribute__((swift_attr("~Copyable"))) NonCopyable { + float x, y; +} NonCopyable; + +typedef struct SWIFT_NONCOPYABLE_WITH_DESTROY(freeNonCopyableWithDeinit) NonCopyableWithDeinit { + void *storage; +} NonCopyableWithDeinit; + +typedef struct __attribute__((swift_attr("destroy:freeCopyableType"))) CopyableType { + void *storage; +} CopyableType; + +typedef struct __attribute__((swift_attr("~Copyable"))) __attribute__((swift_attr("destroy:freeMultiNonCopyable1"))) __attribute__((swift_attr("destroy:freeMultiNonCopyable2"))) MultiNonCopyableType { + void *storage; +} MultiNonCopyableType; + +typedef struct __attribute__((swift_attr("~Copyable"))) __attribute__((swift_attr("destroy:badDestroy1"))) BadDestroyNonCopyableType { + void *storage; +} BadDestroyNonCopyableType; + +typedef struct __attribute__((swift_attr("~Copyable"))) __attribute__((swift_attr("destroy:badDestroy2"))) BadDestroyNonCopyableType2 { + void *storage; +} BadDestroyNonCopyableType2; + +#ifdef __cplusplus +extern "C" { +#endif + +void freeNonCopyableWithDeinit(NonCopyableWithDeinit ncd); +void freeCopyableType(CopyableType ct); + + +void freeMultiNonCopyable1(MultiNonCopyableType ct); +void freeMultiNonCopyable2(MultiNonCopyableType ct); + +void badDestroy1(void); +void badDestroy2(BadDestroyNonCopyableType2 *ptr); + +#ifdef __cplusplus + +struct __attribute__((swift_attr("~Copyable"))) __attribute__((swift_attr("destroy:extraDestroy"))) ExtraDestroy { + void *storage; + + ~ExtraDestroy() { } +}; + +void extraDestroy(ExtraDestroy); +} +#endif diff --git a/test/Interop/C/struct/Inputs/struct-as-option-set.h b/test/Interop/C/struct/Inputs/struct-as-option-set.h new file mode 100644 index 0000000000000..bb50e36b51b2e --- /dev/null +++ b/test/Interop/C/struct/Inputs/struct-as-option-set.h @@ -0,0 +1,6 @@ +#include + +typedef uint32_t WGPUBufferUsage; +static const WGPUBufferUsage WGPUBufferUsage_None = 0x0000000000000000; +static const WGPUBufferUsage WGPUBufferUsage_MapRead = 0x0000000000000001; +static const WGPUBufferUsage WGPUBufferUsage_MapWrite = 0x0000000000000002; diff --git a/test/Interop/C/struct/noncopyable_structs.swift b/test/Interop/C/struct/noncopyable_structs.swift new file mode 100644 index 0000000000000..082632ffffac6 --- /dev/null +++ b/test/Interop/C/struct/noncopyable_structs.swift @@ -0,0 +1,75 @@ +// Check that we get the expected errors for incorrect uses of noncopyable +// imported types with both C and C++ interoperability. + +// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -verify -DERRORS -verify-additional-prefix conly- +// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -verify -DERRORS -DCPLUSPLUS -verify-additional-prefix cplusplus- -cxx-interoperability-mode=default + +// Check that we get the expected SIL +// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -o - | %FileCheck -check-prefix CHECK-SIL %s +// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -o - -cxx-interoperability-mode=default| %FileCheck -check-prefix CHECK-SIL %s + +// Check that we get the expected IR + +// RUN: %target-swift-frontend -emit-ir -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -o - | %FileCheck -check-prefix CHECK-IR %s +// RUN: %target-swift-frontend -emit-ir -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -o - -cxx-interoperability-mode=default | %FileCheck -check-prefix CHECK-IR %s + +import NoncopyableStructs + +// CHECK-IR-LABEL: define hidden swiftcc void @"$s19noncopyable_structs9consumeNCyySo11NonCopyableVnF"(float %0, float %1) #0 { +// CHECK-IR: call ptr @"$sSo11NonCopyableVWOh" +// CHECK-IR: define linkonce_odr hidden ptr @"$sSo11NonCopyableVWOh"(ptr %0) +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret ptr +func consumeNC(_ nc: consuming NonCopyable) { } + +func testNC() { + let nc = NonCopyable() // expected-error{{'nc' consumed more than once}} + consumeNC(nc) // expected-note{{consumed here}} + + #if ERRORS + consumeNC(nc) // expected-note{{consumed again here}} + #endif +} + +func consumeNCWithDeinit(_ nc: consuming NonCopyableWithDeinit) { } + +func testNCWithDeinit() { + let nc = NonCopyableWithDeinit() // expected-error{{'nc' consumed more than once}} + consumeNCWithDeinit(nc) // expected-note{{consumed here}} + + #if ERRORS + consumeNCWithDeinit(nc) // expected-note{{consumed again here}} + #endif +} + +#if ERRORS +// expected-warning@+1{{destroy operation 'freeCopyableType' is only allowed on non-copyable types; did you mean to use SWIFT_NONCOPYABLE?}} +func copyableType(_: CopyableType) { } + +// expected-warning@+1{{'MultiNonCopyableType' is deprecated: multiple destroy operations ('freeMultiNonCopyable1' and 'freeMultiNonCopyable2') provided for type}} +func multiNonCopyableType(_: borrowing MultiNonCopyableType) { } + +// expected-warning@+1{{'BadDestroyNonCopyableType' is deprecated: destroy function 'badDestroy1' must have a single parameter with type 'BadDestroyNonCopyableType'}} +func bad1(_: borrowing BadDestroyNonCopyableType) { } + +// expected-warning@+1{{'BadDestroyNonCopyableType2' is deprecated: destroy function 'badDestroy2' must have a single parameter with type 'BadDestroyNonCopyableType2'}} +func bad2(_: borrowing BadDestroyNonCopyableType2) { } + +#endif + +// CHECK-SIL: sil shared @$sSo21NonCopyableWithDeinitVfD : $@convention(method) (@owned NonCopyableWithDeinit) -> () { +// CHECK-SIL: bb0([[SELF:%[0-9]+]] : $NonCopyableWithDeinit): +// CHECK-SIL: [[SELF_ALLOC:%[0-9]+]] = alloc_stack $NonCopyableWithDeinit +// CHECK-SIL: store [[SELF]] to [[SELF_ALLOC]] +// CHECK-SIL: [[SELF_RELOAD:%[0-9]+]] = load [[SELF_ALLOC]] +// CHECK-SIL: [[FN:%[0-9]+]] = function_ref @{{.*}}freeNonCopyableWithDeinit{{.*}} : $@convention(c) (NonCopyableWithDeinit) -> () +// CHECK-SIL: apply [[FN]]([[SELF_RELOAD]]) : $@convention(c) (NonCopyableWithDeinit) -> () + +// CHECK-IR-LABEL: define hidden swiftcc void @"$s19noncopyable_structs19consumeNCWithDeinityySo015NonCopyableWithE0VnF" +// CHECK-IR: call swiftcc void @"$sSo21NonCopyableWithDeinitVfD" + +// CHECK-IR-LABEL: define {{.*}} swiftcc void @"$sSo21NonCopyableWithDeinitVfD" +// CHECK-IR: {{(call|invoke)}} void @{{.*}}freeNonCopyableWithDeinit + +// CHECK-SIL-LABEL: sil_moveonlydeinit NonCopyableWithDeinit { +// CHECK-SIL: @$sSo21NonCopyableWithDeinitVfD diff --git a/test/Interop/C/struct/noncopyable_structs_nontrivial.swift b/test/Interop/C/struct/noncopyable_structs_nontrivial.swift new file mode 100644 index 0000000000000..2d19a079b8d9a --- /dev/null +++ b/test/Interop/C/struct/noncopyable_structs_nontrivial.swift @@ -0,0 +1,12 @@ +// Split out from noncopyable_structs.swift due to an issue with Windows. + +// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -verify -DERRORS -verify-additional-prefix conly- +// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -verify -DERRORS -DCPLUSPLUS -verify-additional-prefix cplusplus- -cxx-interoperability-mode=default + +// XFAIL: OS=windows-msvc +import NoncopyableStructs + +#if CPLUSPLUS +// expected-cplusplus-warning@+1{{'ExtraDestroy' is deprecated: destroy operation 'extraDestroy' is not allowed on types with a non-trivial destructor}} +func extra(_: borrowing ExtraDestroy) { } +#endif diff --git a/test/Interop/C/struct/typedef_option_set.swift b/test/Interop/C/struct/typedef_option_set.swift new file mode 100644 index 0000000000000..12f2b3fdc1361 --- /dev/null +++ b/test/Interop/C/struct/typedef_option_set.swift @@ -0,0 +1,10 @@ +// RUN: %target-typecheck-verify-swift -I %S/Inputs/ + +import StructAsOptionSet + +func takeOptionSet(_: T) { } + +func useStructAsOptionSet() { + let usage: WGPUBufferUsage = [.mapRead, .mapWrite] + takeOptionSet(usage) +}