Skip to content

Commit f648dbb

Browse files
authored
Merge pull request #84237 from susmonteiro/susmonteiro/cxxrecord-semantics
[cxx-interop] Refactor copyability out of CxxRecordSemantics
2 parents c787253 + 2640c71 commit f648dbb

File tree

5 files changed

+226
-151
lines changed

5 files changed

+226
-151
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,9 @@ class ObjCInterfaceAndImplementationRequest
342342
};
343343

344344
enum class CxxRecordSemanticsKind {
345-
Trivial,
346-
Owned,
347-
MoveOnly,
345+
Value,
348346
Reference,
349347
Iterator,
350-
// A record that is either not copyable/movable or not destructible.
351-
MissingLifetimeOperation,
352-
// A record that has no copy and no move operations
353-
UnavailableConstructors,
354348
// A C++ record that represents a Swift class type exposed to C++ from Swift.
355349
SwiftClassType
356350
};
@@ -576,6 +570,59 @@ class ClangTypeEscapability
576570
void simple_display(llvm::raw_ostream &out, EscapabilityLookupDescriptor desc);
577571
SourceLoc extractNearestSourceLoc(EscapabilityLookupDescriptor desc);
578572

573+
// Swift value semantics of C++ types
574+
// These are usually equivalent, with the exception of references.
575+
// When a reference type is copied, the pointer’s value is copied rather than
576+
// the object’s storage. This means reference types can be imported as
577+
// copyable to Swift, even when they are non-copyable in C++.
578+
enum class CxxValueSemanticsKind {
579+
Copyable,
580+
MoveOnly,
581+
// A record that is either not copyable/movable or not destructible.
582+
MissingLifetimeOperation,
583+
// A record that has no copy and no move operations
584+
UnavailableConstructors,
585+
};
586+
587+
struct CxxValueSemanticsDescriptor final {
588+
const clang::Type *type;
589+
ClangImporter::Implementation *importerImpl;
590+
591+
friend llvm::hash_code hash_value(const CxxValueSemanticsDescriptor &desc) {
592+
return llvm::hash_combine(desc.type);
593+
}
594+
595+
friend bool operator==(const CxxValueSemanticsDescriptor &lhs,
596+
const CxxValueSemanticsDescriptor &rhs) {
597+
return lhs.type == rhs.type;
598+
}
599+
600+
friend bool operator!=(const CxxValueSemanticsDescriptor &lhs,
601+
const CxxValueSemanticsDescriptor &rhs) {
602+
return !(lhs == rhs);
603+
}
604+
};
605+
606+
class CxxValueSemantics
607+
: public SimpleRequest<CxxValueSemantics,
608+
CxxValueSemanticsKind(
609+
CxxValueSemanticsDescriptor),
610+
RequestFlags::Cached> {
611+
public:
612+
using SimpleRequest::SimpleRequest;
613+
614+
bool isCached() const { return true; }
615+
616+
private:
617+
friend SimpleRequest;
618+
619+
CxxValueSemanticsKind evaluate(Evaluator &evaluator,
620+
CxxValueSemanticsDescriptor desc) const;
621+
};
622+
623+
void simple_display(llvm::raw_ostream &out, CxxValueSemanticsDescriptor desc);
624+
SourceLoc extractNearestSourceLoc(CxxValueSemanticsDescriptor desc);
625+
579626
struct CxxDeclExplicitSafetyDescriptor final {
580627
const clang::Decl *decl;
581628
bool isClass;

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ SWIFT_REQUEST(ClangImporter, CustomRefCountingOperation,
4545
SWIFT_REQUEST(ClangImporter, ClangTypeEscapability,
4646
CxxEscapability(EscapabilityLookupDescriptor), Cached,
4747
NoLocationInfo)
48+
SWIFT_REQUEST(ClangImporter, CxxValueSemantics,
49+
CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached,
50+
NoLocationInfo)
4851
SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety,
4952
ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached,
5053
NoLocationInfo)

lib/ClangImporter/ClangImporter.cpp

Lines changed: 74 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -8094,85 +8094,10 @@ bool importer::isViewType(const clang::CXXRecordDecl *decl) {
80948094
return !hasOwnedValueAttr(decl) && hasPointerInSubobjects(decl);
80958095
}
80968096

8097-
static bool copyConstructorIsDefaulted(const clang::CXXRecordDecl *decl) {
8098-
auto ctor = llvm::find_if(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
8099-
return ctor->isCopyConstructor();
8100-
});
8101-
8102-
assert(ctor != decl->ctor_end());
8103-
return ctor->isDefaulted();
8104-
}
8105-
8106-
static bool copyAssignOperatorIsDefaulted(const clang::CXXRecordDecl *decl) {
8107-
auto copyAssignOp = llvm::find_if(decl->decls(), [](clang::Decl *member) {
8108-
if (auto method = dyn_cast<clang::CXXMethodDecl>(member))
8109-
return method->isCopyAssignmentOperator();
8110-
return false;
8111-
});
8112-
8113-
assert(copyAssignOp != decl->decls_end());
8114-
return cast<clang::CXXMethodDecl>(*copyAssignOp)->isDefaulted();
8115-
}
8116-
8117-
/// Recursively checks that there are no user-provided copy constructors or
8118-
/// destructors in any fields or base classes.
8119-
/// Does not check C++ records with specific API annotations.
8120-
static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) {
8121-
// Probably a class template that has not yet been specialized:
8122-
if (!decl->getDefinition())
8123-
return true;
8124-
8125-
if ((decl->hasUserDeclaredCopyConstructor() &&
8126-
!copyConstructorIsDefaulted(decl)) ||
8127-
(decl->hasUserDeclaredCopyAssignment() &&
8128-
!copyAssignOperatorIsDefaulted(decl)) ||
8129-
(decl->hasUserDeclaredDestructor() && decl->getDestructor() &&
8130-
!decl->getDestructor()->isDefaulted()))
8131-
return false;
8132-
8133-
auto checkType = [](clang::QualType t) {
8134-
if (auto recordType = dyn_cast<clang::RecordType>(t.getCanonicalType())) {
8135-
if (auto cxxRecord =
8136-
dyn_cast<clang::CXXRecordDecl>(recordType->getDecl())) {
8137-
if (hasImportAsRefAttr(cxxRecord) || hasOwnedValueAttr(cxxRecord) ||
8138-
hasUnsafeAPIAttr(cxxRecord))
8139-
return true;
8140-
8141-
if (!isSufficientlyTrivial(cxxRecord))
8142-
return false;
8143-
}
8144-
}
8145-
8146-
return true;
8147-
};
8148-
8149-
for (auto field : decl->fields()) {
8150-
if (!checkType(field->getType()))
8151-
return false;
8152-
}
8153-
8154-
for (auto base : decl->bases()) {
8155-
if (!checkType(base.getType()))
8156-
return false;
8157-
}
8158-
8159-
return true;
8160-
}
8161-
8162-
/// Checks if a record provides the required value type lifetime operations
8163-
/// (copy and destroy).
81648097
static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
8165-
// Hack for a base type of std::optional from the Microsoft standard library.
8166-
if (decl->isInStdNamespace() && decl->getIdentifier() &&
8167-
decl->getName() == "_Optional_construct_base")
8168-
return true;
8169-
81708098
if (decl->hasSimpleCopyConstructor())
81718099
return true;
81728100

8173-
// If we have no way of copying the type we can't import the class
8174-
// at all because we cannot express the correct semantics as a swift
8175-
// struct.
81768101
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
81778102
return ctor->isCopyConstructor() && !ctor->isDeleted() &&
81788103
!ctor->isIneligibleOrNotSelected() &&
@@ -8183,12 +8108,10 @@ static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
81838108
}
81848109

81858110
static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
8186-
// If we have no way of copying the type we can't import the class
8187-
// at all because we cannot express the correct semantics as a swift
8188-
// struct.
81898111
if (llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
81908112
return ctor->isMoveConstructor() &&
8191-
(ctor->isDeleted() || ctor->getAccess() != clang::AS_public);
8113+
(ctor->isDeleted() || ctor->isIneligibleOrNotSelected() ||
8114+
ctor->getAccess() != clang::AS_public);
81928115
}))
81938116
return false;
81948117

@@ -8201,7 +8124,8 @@ static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
82018124

82028125
static bool hasDestroyTypeOperations(const clang::CXXRecordDecl *decl) {
82038126
if (auto dtor = decl->getDestructor()) {
8204-
if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) {
8127+
if (dtor->isDeleted() || dtor->isIneligibleOrNotSelected() ||
8128+
dtor->getAccess() != clang::AS_public) {
82058129
return false;
82068130
}
82078131
return true;
@@ -8257,49 +8181,17 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
82578181

82588182
auto cxxDecl = dyn_cast<clang::CXXRecordDecl>(decl);
82598183
if (!cxxDecl) {
8260-
if (hasNonCopyableAttr(decl))
8261-
return CxxRecordSemanticsKind::MoveOnly;
8262-
8263-
return CxxRecordSemanticsKind::Trivial;
8184+
return CxxRecordSemanticsKind::Value;
82648185
}
82658186

82668187
if (isSwiftClassType(cxxDecl))
82678188
return CxxRecordSemanticsKind::SwiftClassType;
82688189

8269-
if (!hasDestroyTypeOperations(cxxDecl) ||
8270-
(!hasCopyTypeOperations(cxxDecl) && !hasMoveTypeOperations(cxxDecl))) {
8271-
8272-
if (hasConstructorWithUnsupportedDefaultArgs(cxxDecl))
8273-
return CxxRecordSemanticsKind::UnavailableConstructors;
8274-
8275-
return CxxRecordSemanticsKind::MissingLifetimeOperation;
8276-
}
8277-
8278-
if (hasNonCopyableAttr(cxxDecl) && hasMoveTypeOperations(cxxDecl)) {
8279-
return CxxRecordSemanticsKind::MoveOnly;
8280-
}
8281-
8282-
if (hasOwnedValueAttr(cxxDecl)) {
8283-
return CxxRecordSemanticsKind::Owned;
8284-
}
8285-
82868190
if (hasIteratorAPIAttr(cxxDecl) || isIterator(cxxDecl)) {
82878191
return CxxRecordSemanticsKind::Iterator;
82888192
}
82898193

8290-
if (hasCopyTypeOperations(cxxDecl)) {
8291-
return CxxRecordSemanticsKind::Owned;
8292-
}
8293-
8294-
if (hasMoveTypeOperations(cxxDecl)) {
8295-
return CxxRecordSemanticsKind::MoveOnly;
8296-
}
8297-
8298-
if (isSufficientlyTrivial(cxxDecl)) {
8299-
return CxxRecordSemanticsKind::Trivial;
8300-
}
8301-
8302-
llvm_unreachable("Could not classify C++ type.");
8194+
return CxxRecordSemanticsKind::Value;
83038195
}
83048196

83058197
ValueDecl *
@@ -8330,6 +8222,74 @@ CxxRecordAsSwiftType::evaluate(Evaluator &evaluator,
83308222
return nullptr;
83318223
}
83328224

8225+
CxxValueSemanticsKind
8226+
CxxValueSemantics::evaluate(Evaluator &evaluator,
8227+
CxxValueSemanticsDescriptor desc) const {
8228+
8229+
const auto *type = desc.type;
8230+
8231+
auto desugared = type->getUnqualifiedDesugaredType();
8232+
const auto *recordType = desugared->getAs<clang::RecordType>();
8233+
if (!recordType)
8234+
return CxxValueSemanticsKind::Copyable;
8235+
8236+
auto recordDecl = recordType->getDecl();
8237+
8238+
// When a reference type is copied, the pointer’s value is copied rather than
8239+
// the object’s storage. This means reference types can be imported as
8240+
// copyable to Swift, even when they are non-copyable in C++.
8241+
if (recordHasReferenceSemantics(recordDecl, desc.importerImpl))
8242+
return CxxValueSemanticsKind::Copyable;
8243+
8244+
if (recordDecl->isInStdNamespace()) {
8245+
// Hack for a base type of std::optional from the Microsoft standard
8246+
// library.
8247+
if (recordDecl->getIdentifier() &&
8248+
recordDecl->getName() == "_Optional_construct_base")
8249+
return CxxValueSemanticsKind::Copyable;
8250+
}
8251+
8252+
const auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl);
8253+
if (!cxxRecordDecl) {
8254+
if (hasNonCopyableAttr(recordDecl))
8255+
return CxxValueSemanticsKind::MoveOnly;
8256+
return CxxValueSemanticsKind::Copyable;
8257+
}
8258+
8259+
bool isCopyable = hasCopyTypeOperations(cxxRecordDecl);
8260+
bool isMovable = hasMoveTypeOperations(cxxRecordDecl);
8261+
8262+
if (!hasDestroyTypeOperations(cxxRecordDecl) || (!isCopyable && !isMovable)) {
8263+
8264+
if (hasConstructorWithUnsupportedDefaultArgs(cxxRecordDecl))
8265+
return CxxValueSemanticsKind::UnavailableConstructors;
8266+
8267+
return CxxValueSemanticsKind::MissingLifetimeOperation;
8268+
}
8269+
8270+
if (hasNonCopyableAttr(cxxRecordDecl) && isMovable)
8271+
return CxxValueSemanticsKind::MoveOnly;
8272+
8273+
if (isCopyable)
8274+
return CxxValueSemanticsKind::Copyable;
8275+
8276+
if (isMovable)
8277+
return CxxValueSemanticsKind::MoveOnly;
8278+
8279+
llvm_unreachable("Could not classify C++ type.");
8280+
}
8281+
8282+
void swift::simple_display(llvm::raw_ostream &out,
8283+
CxxValueSemanticsDescriptor desc) {
8284+
out << "Checking if '";
8285+
out << clang::QualType(desc.type, 0).getAsString();
8286+
out << "' is copyable or movable.";
8287+
}
8288+
8289+
SourceLoc swift::extractNearestSourceLoc(CxxValueSemanticsDescriptor) {
8290+
return SourceLoc();
8291+
}
8292+
83338293
static bool anySubobjectsSelfContained(const clang::CXXRecordDecl *decl) {
83348294
// std::pair and std::tuple might have copy and move constructors, or base
83358295
// classes with copy and move constructors, but they are not self-contained

lib/ClangImporter/ImportDecl.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,8 +2080,8 @@ namespace {
20802080
bool recordHasMoveOnlySemantics(const clang::RecordDecl *decl) {
20812081
auto semanticsKind = evaluateOrDefault(
20822082
Impl.SwiftContext.evaluator,
2083-
CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {});
2084-
return semanticsKind == CxxRecordSemanticsKind::MoveOnly;
2083+
CxxValueSemantics({decl->getTypeForDecl(), &Impl}), {});
2084+
return semanticsKind == CxxValueSemanticsKind::MoveOnly;
20852085
}
20862086

20872087
void markReturnsUnsafeNonescapable(AbstractFunctionDecl *fd) {
@@ -3144,11 +3144,11 @@ namespace {
31443144

31453145
// It is important that we bail on an unimportable record *before* we import
31463146
// any of its members or cache the decl.
3147-
auto semanticsKind = evaluateOrDefault(
3147+
auto valueSemanticsKind = evaluateOrDefault(
31483148
Impl.SwiftContext.evaluator,
3149-
CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {});
3150-
if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation ||
3151-
semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) {
3149+
CxxValueSemantics({decl->getTypeForDecl(), &Impl}), {});
3150+
if (valueSemanticsKind == CxxValueSemanticsKind::MissingLifetimeOperation ||
3151+
valueSemanticsKind == CxxValueSemanticsKind::UnavailableConstructors) {
31523152

31533153
HeaderLoc loc(decl->getLocation());
31543154
if (hasUnsafeAPIAttr(decl))
@@ -3161,7 +3161,7 @@ namespace {
31613161
Impl.diagnose(loc, diag::api_pattern_attr_ignored, "import_iterator",
31623162
decl->getNameAsString());
31633163

3164-
if (semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) {
3164+
if (valueSemanticsKind == CxxValueSemanticsKind::UnavailableConstructors) {
31653165
Impl.addImportDiagnostic(
31663166
decl, Diagnostic(diag::record_unsupported_default_args),
31673167
decl->getLocation());
@@ -3175,7 +3175,12 @@ namespace {
31753175
decl->getLocation());
31763176
return nullptr;
31773177
}
3178-
if (semanticsKind == CxxRecordSemanticsKind::SwiftClassType) {
3178+
3179+
auto cxxRecordsemanticsKind = evaluateOrDefault(
3180+
Impl.SwiftContext.evaluator,
3181+
CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {});
3182+
3183+
if (cxxRecordsemanticsKind == CxxRecordSemanticsKind::SwiftClassType) {
31793184
// FIXME: add a diagnostic here for unsupported imported use of Swift
31803185
// type?
31813186
return nullptr;
@@ -3411,8 +3416,8 @@ namespace {
34113416
decl->getAnonField()->getParent())) {
34123417
auto semanticsKind = evaluateOrDefault(
34133418
Impl.SwiftContext.evaluator,
3414-
CxxRecordSemantics({parent, Impl.SwiftContext, &Impl}), {});
3415-
if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation)
3419+
CxxValueSemantics({parent->getTypeForDecl(), &Impl}), {});
3420+
if (semanticsKind == CxxValueSemanticsKind::MissingLifetimeOperation)
34163421
return nullptr;
34173422
}
34183423

0 commit comments

Comments
 (0)