Skip to content

Commit a10bf21

Browse files
committed
[cxx-interop] 'Support' C++ move only types; fix a few bugs in the object model.
The implemented object model should now match the object model outlined in the Forward Vision document.
1 parent 40a85f7 commit a10bf21

File tree

5 files changed

+103
-12
lines changed

5 files changed

+103
-12
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ ERROR(conforms_to_ambiguous,none,
248248
ERROR(conforms_to_not_protocol,none,
249249
"%0 %1 referenced in protocol conformance '%2' is not a protocol", (DescriptiveDeclKind, ValueDecl *, StringRef))
250250

251+
ERROR(move_only_requires_move_only,none,
252+
"use of move-only C++ type '%0' requires -enable-experimental-move-only",
253+
(StringRef))
254+
251255
NOTE(unsupported_builtin_type, none, "built-in type '%0' not supported", (StringRef))
252256
NOTE(record_field_not_imported, none, "field %0 unavailable (cannot import)", (const clang::NamedDecl*))
253257
NOTE(invoked_func_not_imported, none, "function %0 unavailable (cannot import)", (const clang::NamedDecl*))

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ class ObjCInterfaceAndImplementationRequest
303303
enum class CxxRecordSemanticsKind {
304304
Trivial,
305305
Owned,
306+
MoveOnly,
306307
Reference,
307308
Iterator,
308309
// An API that has be annotated as explicitly unsafe, but still importable.

lib/ClangImporter/ClangImporter.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6479,13 +6479,7 @@ static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) {
64796479

64806480
/// Checks if a record provides the required value type lifetime operations
64816481
/// (copy and destroy).
6482-
static bool hasRequiredValueTypeOperations(const clang::CXXRecordDecl *decl) {
6483-
if (auto dtor = decl->getDestructor()) {
6484-
if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) {
6485-
return false;
6486-
}
6487-
}
6488-
6482+
static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
64896483
// If we have no way of copying the type we can't import the class
64906484
// at all because we cannot express the correct semantics as a swift
64916485
// struct.
@@ -6495,7 +6489,34 @@ static bool hasRequiredValueTypeOperations(const clang::CXXRecordDecl *decl) {
64956489
}))
64966490
return false;
64976491

6498-
return true;
6492+
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
6493+
return ctor->isCopyConstructor();
6494+
});
6495+
}
6496+
6497+
static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
6498+
// If we have no way of copying the type we can't import the class
6499+
// at all because we cannot express the correct semantics as a swift
6500+
// struct.
6501+
if (llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
6502+
return ctor->isMoveConstructor() &&
6503+
(ctor->isDeleted() || ctor->getAccess() != clang::AS_public);
6504+
}))
6505+
return false;
6506+
6507+
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
6508+
return ctor->isMoveConstructor();
6509+
});
6510+
}
6511+
6512+
static bool hasDestroyTypeOperations(const clang::CXXRecordDecl *decl) {
6513+
if (auto dtor = decl->getDestructor()) {
6514+
if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) {
6515+
return false;
6516+
}
6517+
return true;
6518+
}
6519+
return false;
64996520
}
65006521

65016522
static bool isSwiftClassType(const clang::CXXRecordDecl *decl) {
@@ -6538,7 +6559,8 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
65386559
if (isSwiftClassType(cxxDecl))
65396560
return CxxRecordSemanticsKind::SwiftClassType;
65406561

6541-
if (!hasRequiredValueTypeOperations(cxxDecl)) {
6562+
if (!hasDestroyTypeOperations(cxxDecl) ||
6563+
(!hasCopyTypeOperations(cxxDecl) && !hasMoveTypeOperations(cxxDecl))) {
65426564
if (hasUnsafeAPIAttr(cxxDecl))
65436565
desc.ctx.Diags.diagnose({}, diag::api_pattern_attr_ignored,
65446566
"import_unsafe", decl->getNameAsString());
@@ -6564,6 +6586,14 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
65646586
return CxxRecordSemanticsKind::Iterator;
65656587
}
65666588

6589+
if (hasCopyTypeOperations(cxxDecl)) {
6590+
return CxxRecordSemanticsKind::Owned;
6591+
}
6592+
6593+
if (hasMoveTypeOperations(cxxDecl)) {
6594+
return CxxRecordSemanticsKind::MoveOnly;
6595+
}
6596+
65676597
if (hasPointerInSubobjects(cxxDecl)) {
65686598
return CxxRecordSemanticsKind::UnsafePointerMember;
65696599
}
@@ -6572,6 +6602,7 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
65726602
return CxxRecordSemanticsKind::Trivial;
65736603
}
65746604

6605+
// How did we get here?
65756606
return CxxRecordSemanticsKind::Owned;
65766607
}
65776608

lib/ClangImporter/ImportDecl.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,13 @@ namespace {
19661966
return semanticsKind == CxxRecordSemanticsKind::Reference;
19671967
}
19681968

1969+
bool recordHasMoveOnlySemantics(const clang::RecordDecl *decl) {
1970+
auto semanticsKind = evaluateOrDefault(
1971+
Impl.SwiftContext.evaluator,
1972+
CxxRecordSemantics({decl, Impl.SwiftContext}), {});
1973+
return semanticsKind == CxxRecordSemanticsKind::MoveOnly;
1974+
}
1975+
19691976
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
19701977
// Track whether this record contains fields we can't reference in Swift
19711978
// as stored properties.
@@ -2118,6 +2125,20 @@ namespace {
21182125
decl, AccessLevel::Public, loc, name, loc, None, nullptr, dc);
21192126
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result;
21202127

2128+
if (recordHasMoveOnlySemantics(decl)) {
2129+
if (!Impl.SwiftContext.LangOpts.hasFeature(Feature::MoveOnly)) {
2130+
Impl.addImportDiagnostic(
2131+
decl, Diagnostic(
2132+
diag::move_only_requires_move_only,
2133+
Impl.SwiftContext.AllocateCopy(decl->getNameAsString())),
2134+
decl->getLocation());
2135+
return nullptr;
2136+
}
2137+
2138+
result->getAttrs().add(new (Impl.SwiftContext)
2139+
MoveOnlyAttr(/*Implicit=*/true));
2140+
}
2141+
21212142
// FIXME: Figure out what to do with superclasses in C++. One possible
21222143
// solution would be to turn them into members and add conversion
21232144
// functions.
@@ -2573,12 +2594,12 @@ namespace {
25732594
// default). Make sure we only do this if the class has been fully defined
25742595
// and we're not in a dependent context (this is equivalent to the logic
25752596
// in CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp).
2576-
if (decl->getDefinition() && !decl->isBeingDefined() &&
2577-
!decl->isDependentContext()) {
2597+
// TODO: I suspect this if-statement does not need to be here.
2598+
if (!decl->isBeingDefined() && !decl->isDependentContext()) {
25782599
if (decl->needsImplicitDefaultConstructor()) {
25792600
clang::CXXConstructorDecl *ctor =
25802601
clangSema.DeclareImplicitDefaultConstructor(
2581-
const_cast<clang::CXXRecordDecl *>(decl->getDefinition()));
2602+
const_cast<clang::CXXRecordDecl *>(decl));
25822603
if (!ctor->isDeleted())
25832604
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
25842605
ctor);
@@ -2608,6 +2629,12 @@ namespace {
26082629
clangSema.DefineImplicitCopyConstructor(clang::SourceLocation(),
26092630
copyCtor);
26102631
}
2632+
2633+
if (decl->needsImplicitDestructor()) {
2634+
auto dtor = clangSema.DeclareImplicitDestructor(
2635+
const_cast<clang::CXXRecordDecl *>(decl));
2636+
clangSema.DefineImplicitDestructor(clang::SourceLocation(), dtor);
2637+
}
26112638
}
26122639

26132640
// It is import that we bail on an unimportable record *before* we import

lib/IRGen/GenStruct.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,20 @@ namespace {
563563
return nullptr;
564564
}
565565

566+
const clang::CXXConstructorDecl *findMoveConstructor() const {
567+
const clang::CXXRecordDecl *cxxRecordDecl =
568+
dyn_cast<clang::CXXRecordDecl>(ClangDecl);
569+
if (!cxxRecordDecl)
570+
return nullptr;
571+
for (auto method : cxxRecordDecl->methods()) {
572+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(method)) {
573+
if (ctor->isMoveConstructor())
574+
return ctor;
575+
}
576+
}
577+
return nullptr;
578+
}
579+
566580
CanSILFunctionType createCXXCopyConstructorFunctionType(IRGenFunction &IGF,
567581
SILType T) const {
568582
// Create the following function type:
@@ -785,6 +799,13 @@ namespace {
785799

786800
void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
787801
SILType T, bool isOutlined) const override {
802+
if (auto moveConstructor = findMoveConstructor()) {
803+
emitCopyWithCopyConstructor(IGF, T, moveConstructor,
804+
src.getAddress(),
805+
dest.getAddress());
806+
return;
807+
}
808+
788809
if (auto copyConstructor = findCopyConstructor()) {
789810
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
790811
src.getAddress(),
@@ -800,6 +821,13 @@ namespace {
800821

801822
void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T,
802823
bool isOutlined) const override {
824+
if (auto moveConstructor = findMoveConstructor()) {
825+
emitCopyWithCopyConstructor(IGF, T, moveConstructor,
826+
src.getAddress(),
827+
dest.getAddress());
828+
return;
829+
}
830+
803831
if (auto copyConstructor = findCopyConstructor()) {
804832
destroy(IGF, dest, T, isOutlined);
805833
emitCopyWithCopyConstructor(IGF, T, copyConstructor,

0 commit comments

Comments
 (0)