From c3cd9939c1496980fad6f5b07e1ba7001307d183 Mon Sep 17 00:00:00 2001 From: susmonteiro Date: Fri, 31 Oct 2025 10:38:37 +0000 Subject: [PATCH] [cxx-interop] Implicitly defined move constructors --- lib/ClangImporter/ImportDecl.cpp | 15 ++---- lib/IRGen/GenStruct.cpp | 49 ++++++++++--------- .../Cxx/stdlib/Inputs/module.modulemap | 6 +++ test/Interop/Cxx/stdlib/Inputs/std-expected.h | 23 +++++++++ .../stdlib/use-std-expected-typechecker.swift | 22 +++++++++ 5 files changed, 82 insertions(+), 33 deletions(-) create mode 100644 test/Interop/Cxx/stdlib/Inputs/std-expected.h create mode 100644 test/Interop/Cxx/stdlib/use-std-expected-typechecker.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 343a799bd27f5..bc92999caf754 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3105,14 +3105,13 @@ namespace { // instantiate its copy constructor. bool isExplicitlyNonCopyable = hasNonCopyableAttr(decl); - clang::CXXConstructorDecl *moveCtor = nullptr; clang::CXXConstructorDecl *defaultCtor = nullptr; if (decl->needsImplicitCopyConstructor() && !isExplicitlyNonCopyable) { clangSema.DeclareImplicitCopyConstructor( const_cast(decl)); } if (decl->needsImplicitMoveConstructor()) { - moveCtor = clangSema.DeclareImplicitMoveConstructor( + clangSema.DeclareImplicitMoveConstructor( const_cast(decl)); } if (decl->needsImplicitDefaultConstructor()) { @@ -3129,20 +3128,13 @@ namespace { // Note: we use "doesThisDeclarationHaveABody" here because // that's what "DefineImplicitCopyConstructor" checks. !declCtor->doesThisDeclarationHaveABody()) { - if (declCtor->isMoveConstructor()) { - if (!moveCtor) - moveCtor = declCtor; - } else if (declCtor->isDefaultConstructor()) { + if (declCtor->isDefaultConstructor()) { if (!defaultCtor) defaultCtor = declCtor; } } } } - if (moveCtor && !decl->isAnonymousStructOrUnion()) { - clangSema.DefineImplicitMoveConstructor(clang::SourceLocation(), - moveCtor); - } if (defaultCtor) { clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(), defaultCtor); @@ -3151,7 +3143,8 @@ namespace { if (decl->needsImplicitDestructor()) { auto dtor = clangSema.DeclareImplicitDestructor( const_cast(decl)); - clangSema.DefineImplicitDestructor(clang::SourceLocation(), dtor); + if (!dtor->isDeleted() && !dtor->isIneligibleOrNotSelected()) + clangSema.DefineImplicitDestructor(clang::SourceLocation(), dtor); } } diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index ff94867085d89..2c789392fe8a0 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -611,7 +611,7 @@ namespace { /*invocation subs*/ SubstitutionMap(), IGF.IGM.Context); } - void emitCopyWithCopyConstructor( + void emitCopyWithCopyOrMoveConstructor( IRGenFunction &IGF, SILType T, const clang::CXXConstructorDecl *copyConstructor, llvm::Value *src, llvm::Value *dest) const { @@ -625,12 +625,21 @@ namespace { if (copyConstructor->isDefaulted() && copyConstructor->getAccess() == clang::AS_public && !copyConstructor->isDeleted() && + !copyConstructor->isIneligibleOrNotSelected() && // Note: we use "doesThisDeclarationHaveABody" here because // that's what "DefineImplicitCopyConstructor" checks. !copyConstructor->doesThisDeclarationHaveABody()) { - importer->getClangSema().DefineImplicitCopyConstructor( - clang::SourceLocation(), - const_cast(copyConstructor)); + assert(!copyConstructor->getParent()->isAnonymousStructOrUnion() && + "Cannot do codegen of special member functions of anonymous " + "structs/unions"); + if (copyConstructor->isCopyConstructor()) + importer->getClangSema().DefineImplicitCopyConstructor( + clang::SourceLocation(), + const_cast(copyConstructor)); + else + importer->getClangSema().DefineImplicitMoveConstructor( + clang::SourceLocation(), + const_cast(copyConstructor)); } auto &diagEngine = importer->getClangSema().getDiagnostics(); @@ -812,9 +821,9 @@ namespace { Address srcAddr, SILType T, bool isOutlined) const override { if (auto copyConstructor = findCopyConstructor()) { - emitCopyWithCopyConstructor(IGF, T, copyConstructor, - srcAddr.getAddress(), - destAddr.getAddress()); + emitCopyWithCopyOrMoveConstructor(IGF, T, copyConstructor, + srcAddr.getAddress(), + destAddr.getAddress()); return; } StructTypeInfoBase +#include + +using NonCopyableExpected = std::expected, int>; + +template +class UniqueRef { +public: + std::unique_ptr _field; +}; + +struct Decoder {}; +enum Error { + DoomA, + DoomB +}; + +using DecoderOrError = std::expected, Error>; + +#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_UNIQUE_PTR_H diff --git a/test/Interop/Cxx/stdlib/use-std-expected-typechecker.swift b/test/Interop/Cxx/stdlib/use-std-expected-typechecker.swift new file mode 100644 index 0000000000000..1c5fe02bf76e0 --- /dev/null +++ b/test/Interop/Cxx/stdlib/use-std-expected-typechecker.swift @@ -0,0 +1,22 @@ +// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -Xcc -std=c++23 -diagnostic-style llvm 2>&1 | %FileCheck %s + +// TODO not yet supported with libstdc++ +// XFAIL: OS=linux-gnu + +// https://github.com/apple/swift/issues/70226 +// UNSUPPORTED: OS=windows-msvc + +import StdExpected +import CxxStdlib + +func takeCopyable(_ x: T) {} + +let nonCopExpected = NonCopyableExpected() +takeCopyable(nonCopExpected) +// CHECK: error: global function 'takeCopyable' requires that 'NonCopyableExpected' (aka {{.*}}) conform to 'Copyable' + +let doe = DecoderOrError() +takeCopyable(doe) +// CHECK: error: global function 'takeCopyable' requires that 'DecoderOrError' (aka {{.*}}) conform to 'Copyable' + +// CHECK-NOT: error