Skip to content

Commit e54752a

Browse files
authored
Merge pull request #84517 from susmonteiro/susmonteiro/noncopyable-attr
[cxx-interop] SWIFT_NONCOPYABLE overlooked in some cases
2 parents 761b88f + 7e14a8e commit e54752a

File tree

5 files changed

+76
-53
lines changed

5 files changed

+76
-53
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8232,17 +8232,15 @@ static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
82328232
}
82338233

82348234
static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
8235-
if (llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
8236-
return ctor->isMoveConstructor() &&
8237-
(ctor->isDeleted() || ctor->isIneligibleOrNotSelected() ||
8238-
ctor->getAccess() != clang::AS_public);
8239-
}))
8240-
return false;
8235+
if (decl->hasSimpleMoveConstructor())
8236+
return true;
82418237

82428238
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
8243-
return ctor->isMoveConstructor() &&
8239+
return ctor->isMoveConstructor() && !ctor->isDeleted() &&
8240+
!ctor->isIneligibleOrNotSelected() &&
82448241
// FIXME: Support default arguments (rdar://142414553)
8245-
ctor->getNumParams() == 1;
8242+
ctor->getNumParams() == 1 &&
8243+
ctor->getAccess() == clang::AS_public;
82468244
});
82478245
}
82488246

@@ -8374,46 +8372,48 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
83748372
return CxxValueSemanticsKind::Copyable;
83758373
}
83768374

8377-
auto injectedStlAnnotation =
8378-
recordDecl->isInStdNamespace()
8379-
? STLConditionalParams.find(recordDecl->getName())
8380-
: STLConditionalParams.end();
8381-
auto STLParams = injectedStlAnnotation != STLConditionalParams.end()
8382-
? injectedStlAnnotation->second
8383-
: std::vector<int>();
8384-
auto conditionalParams = getConditionalCopyableAttrParams(recordDecl);
8385-
8386-
if (!STLParams.empty() || !conditionalParams.empty()) {
8387-
HeaderLoc loc{recordDecl->getLocation()};
8388-
std::function checkArgValueSemantics =
8389-
[&](clang::TemplateArgument &arg,
8390-
StringRef argToCheck) -> std::optional<CxxValueSemanticsKind> {
8391-
if (arg.getKind() != clang::TemplateArgument::Type && importerImpl) {
8392-
importerImpl->diagnose(loc, diag::type_template_parameter_expected,
8393-
argToCheck);
8394-
return CxxValueSemanticsKind::Unknown;
8395-
}
8375+
if (!hasNonCopyableAttr(recordDecl)) {
8376+
auto injectedStlAnnotation =
8377+
recordDecl->isInStdNamespace()
8378+
? STLConditionalParams.find(recordDecl->getName())
8379+
: STLConditionalParams.end();
8380+
auto STLParams = injectedStlAnnotation != STLConditionalParams.end()
8381+
? injectedStlAnnotation->second
8382+
: std::vector<int>();
8383+
auto conditionalParams = getConditionalCopyableAttrParams(recordDecl);
83968384

8397-
auto argValueSemantics = evaluateOrDefault(
8398-
evaluator,
8399-
CxxValueSemantics(
8400-
{arg.getAsType()->getUnqualifiedDesugaredType(), importerImpl}),
8401-
{});
8402-
if (argValueSemantics != CxxValueSemanticsKind::Copyable)
8403-
return argValueSemantics;
8404-
return std::nullopt;
8405-
};
8385+
if (!STLParams.empty() || !conditionalParams.empty()) {
8386+
HeaderLoc loc{recordDecl->getLocation()};
8387+
std::function checkArgValueSemantics =
8388+
[&](clang::TemplateArgument &arg,
8389+
StringRef argToCheck) -> std::optional<CxxValueSemanticsKind> {
8390+
if (arg.getKind() != clang::TemplateArgument::Type && importerImpl) {
8391+
importerImpl->diagnose(loc, diag::type_template_parameter_expected,
8392+
argToCheck);
8393+
return CxxValueSemanticsKind::Unknown;
8394+
}
84068395

8407-
auto result = checkConditionalParams<CxxValueSemanticsKind>(
8408-
recordDecl, STLParams, conditionalParams, checkArgValueSemantics);
8409-
if (result.has_value())
8410-
return result.value();
8396+
auto argValueSemantics = evaluateOrDefault(
8397+
evaluator,
8398+
CxxValueSemantics(
8399+
{arg.getAsType()->getUnqualifiedDesugaredType(), importerImpl}),
8400+
{});
8401+
if (argValueSemantics != CxxValueSemanticsKind::Copyable)
8402+
return argValueSemantics;
8403+
return std::nullopt;
8404+
};
84118405

8412-
if (importerImpl)
8413-
for (auto name : conditionalParams)
8414-
importerImpl->diagnose(loc, diag::unknown_template_parameter, name);
8406+
auto result = checkConditionalParams<CxxValueSemanticsKind>(
8407+
recordDecl, STLParams, conditionalParams, checkArgValueSemantics);
8408+
if (result.has_value())
8409+
return result.value();
84158410

8416-
return CxxValueSemanticsKind::Copyable;
8411+
if (importerImpl)
8412+
for (auto name : conditionalParams)
8413+
importerImpl->diagnose(loc, diag::unknown_template_parameter, name);
8414+
8415+
return CxxValueSemanticsKind::Copyable;
8416+
}
84178417
}
84188418

84198419
const auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl);
@@ -8423,7 +8423,8 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
84238423
return CxxValueSemanticsKind::Copyable;
84248424
}
84258425

8426-
bool isCopyable = hasCopyTypeOperations(cxxRecordDecl);
8426+
bool isCopyable = !hasNonCopyableAttr(cxxRecordDecl) &&
8427+
hasCopyTypeOperations(cxxRecordDecl);
84278428
bool isMovable = hasMoveTypeOperations(cxxRecordDecl);
84288429

84298430
if (!hasDestroyTypeOperations(cxxRecordDecl) || (!isCopyable && !isMovable)) {

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,10 +3011,8 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy(
30113011
ctx.evaluator,
30123012
CxxValueSemantics({clangType->getTypeForDecl(), &ImporterImpl}), {});
30133013

3014-
if (valueSemanticsKind == CxxValueSemanticsKind::MoveOnly)
3015-
return destroyFunc;
3016-
3017-
if (valueSemanticsKind != CxxValueSemanticsKind::Copyable)
3014+
if (valueSemanticsKind != CxxValueSemanticsKind::Copyable &&
3015+
valueSemanticsKind != CxxValueSemanticsKind::MoveOnly)
30183016
return nullptr;
30193017

30203018
auto cxxRecordSemanticsKind = evaluateOrDefault(
@@ -3033,6 +3031,9 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy(
30333031
}
30343032
}
30353033

3034+
if (valueSemanticsKind == CxxValueSemanticsKind::MoveOnly)
3035+
return destroyFunc;
3036+
30363037
markDeprecated(
30373038
nominal,
30383039
"destroy operation '" +

test/Interop/C/struct/Inputs/noncopyable-struct.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ void badDestroy2(BadDestroyNonCopyableType2 *ptr);
4747

4848
struct __attribute__((swift_attr("~Copyable"))) __attribute__((swift_attr("destroy:extraDestroy"))) ExtraDestroy {
4949
void *storage;
50-
50+
ExtraDestroy(ExtraDestroy&&) = default;
5151
~ExtraDestroy() { }
5252
};
5353

test/Interop/C/struct/noncopyable_structs_nontrivial.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -verify -DERRORS -verify-additional-prefix conly-
44
// RUN: %target-swift-frontend -emit-sil -I %S/Inputs/ -I %swift_src_root/lib/ClangImporter/SwiftBridging %s -verify -DERRORS -DCPLUSPLUS -verify-additional-prefix cplusplus- -cxx-interoperability-mode=default
55

6-
// XFAIL: OS=windows-msvc
76
import NoncopyableStructs
87

98
#if CPLUSPLUS

test/Interop/Cxx/class/noncopyable-typechecker.swift

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: split-file %s %t
3-
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs %t/test.swift
4-
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -Xcc -std=c++20 -verify-additional-prefix cpp20- -D CPP20 -typecheck -verify -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs %t/test.swift
3+
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t%{fs-sep}Inputs %t%{fs-sep}test.swift -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}noncopyable.h
4+
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -Xcc -std=c++20 -verify-additional-prefix cpp20- -D CPP20 -typecheck -verify -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t%{fs-sep}Inputs %t%{fs-sep}test.swift -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}noncopyable.h
55

66
//--- Inputs/module.modulemap
77
module Test {
@@ -68,6 +68,18 @@ MyPair<int, NonCopyableRequires> p7();
6868

6969
#endif
7070

71+
template<typename T>
72+
struct SWIFT_COPYABLE_IF(T) SWIFT_NONCOPYABLE DoubleAnnotation {};
73+
74+
using DoubleAnnotationInt = DoubleAnnotation<int>;
75+
76+
struct SWIFT_NONCOPYABLE NonCopyableNonMovable { // expected-note {{record 'NonCopyableNonMovable' is not automatically available: it must have a copy/move constructor and a destructor; does this type have reference semantics?}}
77+
NonCopyableNonMovable() {}
78+
NonCopyableNonMovable(const NonCopyableNonMovable& other) {}
79+
NonCopyableNonMovable(NonCopyableNonMovable&& other) = delete;
80+
};
81+
82+
7183
//--- test.swift
7284
import Test
7385
import CxxStdlib
@@ -107,3 +119,13 @@ func useOfRequires() {
107119
takeCopyable(p7()) // expected-cpp20-error {{global function 'takeCopyable' requires that 'MyPair<CInt, RequiresCopyableT<NonCopyable>>' conform to 'Copyable'}}
108120
}
109121
#endif
122+
123+
func doubleAnnotation() {
124+
let s = DoubleAnnotationInt()
125+
takeCopyable(s) // expected-error {{global function 'takeCopyable' requires that 'DoubleAnnotationInt' (aka 'DoubleAnnotation<CInt>') conform to 'Copyable'}}
126+
}
127+
128+
func missingLifetimeOperation() {
129+
let s = NonCopyableNonMovable() // expected-error {{cannot find 'NonCopyableNonMovable' in scope}}
130+
takeCopyable(s)
131+
}

0 commit comments

Comments
 (0)