diff --git a/include/swift/ClangImporter/ClangImporterRequests.h b/include/swift/ClangImporter/ClangImporterRequests.h index 8a3a88acae1d3..cebaf06ee8a63 100644 --- a/include/swift/ClangImporter/ClangImporterRequests.h +++ b/include/swift/ClangImporter/ClangImporterRequests.h @@ -342,15 +342,9 @@ class ObjCInterfaceAndImplementationRequest }; enum class CxxRecordSemanticsKind { - Trivial, - Owned, - MoveOnly, + Value, Reference, Iterator, - // A record that is either not copyable/movable or not destructible. - MissingLifetimeOperation, - // A record that has no copy and no move operations - UnavailableConstructors, // A C++ record that represents a Swift class type exposed to C++ from Swift. SwiftClassType }; @@ -576,6 +570,59 @@ class ClangTypeEscapability void simple_display(llvm::raw_ostream &out, EscapabilityLookupDescriptor desc); SourceLoc extractNearestSourceLoc(EscapabilityLookupDescriptor desc); +// Swift value semantics of C++ types +// These are usually equivalent, with the exception of references. +// When a reference type is copied, the pointer’s value is copied rather than +// the object’s storage. This means reference types can be imported as +// copyable to Swift, even when they are non-copyable in C++. +enum class CxxValueSemanticsKind { + Copyable, + MoveOnly, + // A record that is either not copyable/movable or not destructible. + MissingLifetimeOperation, + // A record that has no copy and no move operations + UnavailableConstructors, +}; + +struct CxxValueSemanticsDescriptor final { + const clang::Type *type; + ClangImporter::Implementation *importerImpl; + + friend llvm::hash_code hash_value(const CxxValueSemanticsDescriptor &desc) { + return llvm::hash_combine(desc.type); + } + + friend bool operator==(const CxxValueSemanticsDescriptor &lhs, + const CxxValueSemanticsDescriptor &rhs) { + return lhs.type == rhs.type; + } + + friend bool operator!=(const CxxValueSemanticsDescriptor &lhs, + const CxxValueSemanticsDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +class CxxValueSemantics + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + + bool isCached() const { return true; } + +private: + friend SimpleRequest; + + CxxValueSemanticsKind evaluate(Evaluator &evaluator, + CxxValueSemanticsDescriptor desc) const; +}; + +void simple_display(llvm::raw_ostream &out, CxxValueSemanticsDescriptor desc); +SourceLoc extractNearestSourceLoc(CxxValueSemanticsDescriptor desc); + struct CxxDeclExplicitSafetyDescriptor final { const clang::Decl *decl; bool isClass; diff --git a/include/swift/ClangImporter/ClangImporterTypeIDZone.def b/include/swift/ClangImporter/ClangImporterTypeIDZone.def index 2d81f93181ac3..0d562311a9c72 100644 --- a/include/swift/ClangImporter/ClangImporterTypeIDZone.def +++ b/include/swift/ClangImporter/ClangImporterTypeIDZone.def @@ -45,6 +45,9 @@ SWIFT_REQUEST(ClangImporter, CustomRefCountingOperation, SWIFT_REQUEST(ClangImporter, ClangTypeEscapability, CxxEscapability(EscapabilityLookupDescriptor), Cached, NoLocationInfo) +SWIFT_REQUEST(ClangImporter, CxxValueSemantics, + CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached, + NoLocationInfo) SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety, ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached, NoLocationInfo) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index c816ebef3059e..6182b43e83a50 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -8094,85 +8094,10 @@ bool importer::isViewType(const clang::CXXRecordDecl *decl) { return !hasOwnedValueAttr(decl) && hasPointerInSubobjects(decl); } -static bool copyConstructorIsDefaulted(const clang::CXXRecordDecl *decl) { - auto ctor = llvm::find_if(decl->ctors(), [](clang::CXXConstructorDecl *ctor) { - return ctor->isCopyConstructor(); - }); - - assert(ctor != decl->ctor_end()); - return ctor->isDefaulted(); -} - -static bool copyAssignOperatorIsDefaulted(const clang::CXXRecordDecl *decl) { - auto copyAssignOp = llvm::find_if(decl->decls(), [](clang::Decl *member) { - if (auto method = dyn_cast(member)) - return method->isCopyAssignmentOperator(); - return false; - }); - - assert(copyAssignOp != decl->decls_end()); - return cast(*copyAssignOp)->isDefaulted(); -} - -/// Recursively checks that there are no user-provided copy constructors or -/// destructors in any fields or base classes. -/// Does not check C++ records with specific API annotations. -static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) { - // Probably a class template that has not yet been specialized: - if (!decl->getDefinition()) - return true; - - if ((decl->hasUserDeclaredCopyConstructor() && - !copyConstructorIsDefaulted(decl)) || - (decl->hasUserDeclaredCopyAssignment() && - !copyAssignOperatorIsDefaulted(decl)) || - (decl->hasUserDeclaredDestructor() && decl->getDestructor() && - !decl->getDestructor()->isDefaulted())) - return false; - - auto checkType = [](clang::QualType t) { - if (auto recordType = dyn_cast(t.getCanonicalType())) { - if (auto cxxRecord = - dyn_cast(recordType->getDecl())) { - if (hasImportAsRefAttr(cxxRecord) || hasOwnedValueAttr(cxxRecord) || - hasUnsafeAPIAttr(cxxRecord)) - return true; - - if (!isSufficientlyTrivial(cxxRecord)) - return false; - } - } - - return true; - }; - - for (auto field : decl->fields()) { - if (!checkType(field->getType())) - return false; - } - - for (auto base : decl->bases()) { - if (!checkType(base.getType())) - return false; - } - - return true; -} - -/// Checks if a record provides the required value type lifetime operations -/// (copy and destroy). static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) { - // Hack for a base type of std::optional from the Microsoft standard library. - if (decl->isInStdNamespace() && decl->getIdentifier() && - decl->getName() == "_Optional_construct_base") - return true; - if (decl->hasSimpleCopyConstructor()) return true; - // If we have no way of copying the type we can't import the class - // at all because we cannot express the correct semantics as a swift - // struct. return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) { return ctor->isCopyConstructor() && !ctor->isDeleted() && !ctor->isIneligibleOrNotSelected() && @@ -8183,12 +8108,10 @@ static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) { } static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) { - // If we have no way of copying the type we can't import the class - // at all because we cannot express the correct semantics as a swift - // struct. if (llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) { return ctor->isMoveConstructor() && - (ctor->isDeleted() || ctor->getAccess() != clang::AS_public); + (ctor->isDeleted() || ctor->isIneligibleOrNotSelected() || + ctor->getAccess() != clang::AS_public); })) return false; @@ -8201,7 +8124,8 @@ static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) { static bool hasDestroyTypeOperations(const clang::CXXRecordDecl *decl) { if (auto dtor = decl->getDestructor()) { - if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) { + if (dtor->isDeleted() || dtor->isIneligibleOrNotSelected() || + dtor->getAccess() != clang::AS_public) { return false; } return true; @@ -8257,49 +8181,17 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator, auto cxxDecl = dyn_cast(decl); if (!cxxDecl) { - if (hasNonCopyableAttr(decl)) - return CxxRecordSemanticsKind::MoveOnly; - - return CxxRecordSemanticsKind::Trivial; + return CxxRecordSemanticsKind::Value; } if (isSwiftClassType(cxxDecl)) return CxxRecordSemanticsKind::SwiftClassType; - if (!hasDestroyTypeOperations(cxxDecl) || - (!hasCopyTypeOperations(cxxDecl) && !hasMoveTypeOperations(cxxDecl))) { - - if (hasConstructorWithUnsupportedDefaultArgs(cxxDecl)) - return CxxRecordSemanticsKind::UnavailableConstructors; - - return CxxRecordSemanticsKind::MissingLifetimeOperation; - } - - if (hasNonCopyableAttr(cxxDecl) && hasMoveTypeOperations(cxxDecl)) { - return CxxRecordSemanticsKind::MoveOnly; - } - - if (hasOwnedValueAttr(cxxDecl)) { - return CxxRecordSemanticsKind::Owned; - } - if (hasIteratorAPIAttr(cxxDecl) || isIterator(cxxDecl)) { return CxxRecordSemanticsKind::Iterator; } - if (hasCopyTypeOperations(cxxDecl)) { - return CxxRecordSemanticsKind::Owned; - } - - if (hasMoveTypeOperations(cxxDecl)) { - return CxxRecordSemanticsKind::MoveOnly; - } - - if (isSufficientlyTrivial(cxxDecl)) { - return CxxRecordSemanticsKind::Trivial; - } - - llvm_unreachable("Could not classify C++ type."); + return CxxRecordSemanticsKind::Value; } ValueDecl * @@ -8330,6 +8222,74 @@ CxxRecordAsSwiftType::evaluate(Evaluator &evaluator, return nullptr; } +CxxValueSemanticsKind +CxxValueSemantics::evaluate(Evaluator &evaluator, + CxxValueSemanticsDescriptor desc) const { + + const auto *type = desc.type; + + auto desugared = type->getUnqualifiedDesugaredType(); + const auto *recordType = desugared->getAs(); + if (!recordType) + return CxxValueSemanticsKind::Copyable; + + auto recordDecl = recordType->getDecl(); + + // When a reference type is copied, the pointer’s value is copied rather than + // the object’s storage. This means reference types can be imported as + // copyable to Swift, even when they are non-copyable in C++. + if (recordHasReferenceSemantics(recordDecl, desc.importerImpl)) + return CxxValueSemanticsKind::Copyable; + + if (recordDecl->isInStdNamespace()) { + // Hack for a base type of std::optional from the Microsoft standard + // library. + if (recordDecl->getIdentifier() && + recordDecl->getName() == "_Optional_construct_base") + return CxxValueSemanticsKind::Copyable; + } + + const auto cxxRecordDecl = dyn_cast(recordDecl); + if (!cxxRecordDecl) { + if (hasNonCopyableAttr(recordDecl)) + return CxxValueSemanticsKind::MoveOnly; + return CxxValueSemanticsKind::Copyable; + } + + bool isCopyable = hasCopyTypeOperations(cxxRecordDecl); + bool isMovable = hasMoveTypeOperations(cxxRecordDecl); + + if (!hasDestroyTypeOperations(cxxRecordDecl) || (!isCopyable && !isMovable)) { + + if (hasConstructorWithUnsupportedDefaultArgs(cxxRecordDecl)) + return CxxValueSemanticsKind::UnavailableConstructors; + + return CxxValueSemanticsKind::MissingLifetimeOperation; + } + + if (hasNonCopyableAttr(cxxRecordDecl) && isMovable) + return CxxValueSemanticsKind::MoveOnly; + + if (isCopyable) + return CxxValueSemanticsKind::Copyable; + + if (isMovable) + return CxxValueSemanticsKind::MoveOnly; + + llvm_unreachable("Could not classify C++ type."); +} + +void swift::simple_display(llvm::raw_ostream &out, + CxxValueSemanticsDescriptor desc) { + out << "Checking if '"; + out << clang::QualType(desc.type, 0).getAsString(); + out << "' is copyable or movable."; +} + +SourceLoc swift::extractNearestSourceLoc(CxxValueSemanticsDescriptor) { + return SourceLoc(); +} + static bool anySubobjectsSelfContained(const clang::CXXRecordDecl *decl) { // std::pair and std::tuple might have copy and move constructors, or base // classes with copy and move constructors, but they are not self-contained diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index f000dbdda83cb..b272dcc9e4bc0 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2080,8 +2080,8 @@ namespace { bool recordHasMoveOnlySemantics(const clang::RecordDecl *decl) { auto semanticsKind = evaluateOrDefault( Impl.SwiftContext.evaluator, - CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {}); - return semanticsKind == CxxRecordSemanticsKind::MoveOnly; + CxxValueSemantics({decl->getTypeForDecl(), &Impl}), {}); + return semanticsKind == CxxValueSemanticsKind::MoveOnly; } void markReturnsUnsafeNonescapable(AbstractFunctionDecl *fd) { @@ -3144,11 +3144,11 @@ namespace { // It is important that we bail on an unimportable record *before* we import // any of its members or cache the decl. - auto semanticsKind = evaluateOrDefault( + auto valueSemanticsKind = evaluateOrDefault( Impl.SwiftContext.evaluator, - CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {}); - if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation || - semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) { + CxxValueSemantics({decl->getTypeForDecl(), &Impl}), {}); + if (valueSemanticsKind == CxxValueSemanticsKind::MissingLifetimeOperation || + valueSemanticsKind == CxxValueSemanticsKind::UnavailableConstructors) { HeaderLoc loc(decl->getLocation()); if (hasUnsafeAPIAttr(decl)) @@ -3161,7 +3161,7 @@ namespace { Impl.diagnose(loc, diag::api_pattern_attr_ignored, "import_iterator", decl->getNameAsString()); - if (semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) { + if (valueSemanticsKind == CxxValueSemanticsKind::UnavailableConstructors) { Impl.addImportDiagnostic( decl, Diagnostic(diag::record_unsupported_default_args), decl->getLocation()); @@ -3175,7 +3175,12 @@ namespace { decl->getLocation()); return nullptr; } - if (semanticsKind == CxxRecordSemanticsKind::SwiftClassType) { + + auto cxxRecordsemanticsKind = evaluateOrDefault( + Impl.SwiftContext.evaluator, + CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {}); + + if (cxxRecordsemanticsKind == CxxRecordSemanticsKind::SwiftClassType) { // FIXME: add a diagnostic here for unsupported imported use of Swift // type? return nullptr; @@ -3411,8 +3416,8 @@ namespace { decl->getAnonField()->getParent())) { auto semanticsKind = evaluateOrDefault( Impl.SwiftContext.evaluator, - CxxRecordSemantics({parent, Impl.SwiftContext, &Impl}), {}); - if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation) + CxxValueSemantics({parent->getTypeForDecl(), &Impl}), {}); + if (semanticsKind == CxxValueSemanticsKind::MissingLifetimeOperation) return nullptr; } diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 36b83a7bb7d88..9685b962d5fd0 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -2864,6 +2864,71 @@ static void markDeprecated(Decl *decl, llvm::Twine message) { ctx, ctx.AllocateCopy(message.str()))); } +static bool copyConstructorIsDefaulted(const clang::CXXRecordDecl *decl) { + auto ctor = llvm::find_if(decl->ctors(), [](clang::CXXConstructorDecl *ctor) { + return ctor->isCopyConstructor(); + }); + + assert(ctor != decl->ctor_end()); + return ctor->isDefaulted(); +} + +static bool copyAssignOperatorIsDefaulted(const clang::CXXRecordDecl *decl) { + auto copyAssignOp = llvm::find_if(decl->decls(), [](clang::Decl *member) { + if (auto method = dyn_cast(member)) + return method->isCopyAssignmentOperator(); + return false; + }); + + assert(copyAssignOp != decl->decls_end()); + return cast(*copyAssignOp)->isDefaulted(); +} + +/// Recursively checks that there are no user-provided copy constructors or +/// destructors in any fields or base classes. +/// Does not check C++ records with specific API annotations. +static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) { + // Probably a class template that has not yet been specialized: + if (!decl->getDefinition()) + return true; + + if ((decl->hasUserDeclaredCopyConstructor() && + !copyConstructorIsDefaulted(decl)) || + (decl->hasUserDeclaredCopyAssignment() && + !copyAssignOperatorIsDefaulted(decl)) || + (decl->hasUserDeclaredDestructor() && decl->getDestructor() && + !decl->getDestructor()->isDefaulted())) + return false; + + auto checkType = [](clang::QualType t) { + if (auto recordType = dyn_cast(t.getCanonicalType())) { + if (auto cxxRecord = + dyn_cast(recordType->getDecl())) { + if (hasImportAsRefAttr(cxxRecord) || hasOwnedValueAttr(cxxRecord) || + hasUnsafeAPIAttr(cxxRecord)) + return true; + + if (!isSufficientlyTrivial(cxxRecord)) + return false; + } + } + + return true; + }; + + for (auto field : decl->fields()) { + if (!checkType(field->getType())) + return false; + } + + for (auto base : decl->bases()) { + if (!checkType(base.getType())) + return false; + } + + return true; +} + /// Find an explicitly-provided "destroy" operation specified for the /// given Clang type and return it. FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy( @@ -2943,14 +3008,23 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy( // 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: + auto valueSemanticsKind = evaluateOrDefault( + ctx.evaluator, + CxxValueSemantics({clangType->getTypeForDecl(), &ImporterImpl}), {}); + + if (valueSemanticsKind == CxxValueSemanticsKind::MoveOnly) + return destroyFunc; + + if (valueSemanticsKind != CxxValueSemanticsKind::Copyable) + return nullptr; + + auto cxxRecordSemanticsKind = evaluateOrDefault( + ctx.evaluator, CxxRecordSemantics({clangType, ctx, &ImporterImpl}), {}); + switch (cxxRecordSemanticsKind) { + case CxxRecordSemanticsKind::Value: case CxxRecordSemanticsKind::Reference: if (auto cxxRecord = dyn_cast(clangType)) { - if (!cxxRecord->hasTrivialDestructor()) { + if (!isSufficientlyTrivial(cxxRecord)) { markDeprecated( nominal, "destroy operation '" + @@ -2960,9 +3034,6 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy( } } - LLVM_FALLTHROUGH; - - case CxxRecordSemanticsKind::Trivial: markDeprecated( nominal, "destroy operation '" + @@ -2972,20 +3043,9 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy( 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