Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 54 additions & 7 deletions include/swift/ClangImporter/ClangImporterRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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<CxxValueSemantics,
CxxValueSemanticsKind(
CxxValueSemanticsDescriptor),
RequestFlags::Cached> {
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;
Expand Down
3 changes: 3 additions & 0 deletions include/swift/ClangImporter/ClangImporterTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
188 changes: 74 additions & 114 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<clang::CXXMethodDecl>(member))
return method->isCopyAssignmentOperator();
return false;
});

assert(copyAssignOp != decl->decls_end());
return cast<clang::CXXMethodDecl>(*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<clang::RecordType>(t.getCanonicalType())) {
if (auto cxxRecord =
dyn_cast<clang::CXXRecordDecl>(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() &&
Expand All @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -8257,49 +8181,17 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,

auto cxxDecl = dyn_cast<clang::CXXRecordDecl>(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 *
Expand Down Expand Up @@ -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<clang::RecordType>();
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<clang::CXXRecordDecl>(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
Expand Down
25 changes: 15 additions & 10 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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))
Expand All @@ -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());
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
Loading