diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1e738bd7201bd..7a7d34cdc3316 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6025,6 +6025,8 @@ NOTE(not_objc_error_protocol_composition,none, "in Objective-C", ()) NOTE(not_objc_empty_tuple,none, "empty tuple type cannot be represented in Objective-C", ()) +NOTE(not_objc_non_trivial_cxx_class,none, + "non-trivial C++ classes cannot be represented in Objective-C", ()) NOTE(not_objc_tuple,none, "tuples cannot be represented in Objective-C", ()) NOTE(not_objc_swift_class,none, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d0e42c274251a..4e5a6c7e95c65 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3096,6 +3096,17 @@ getForeignRepresentable(Type type, ForeignLanguage language, case ForeignLanguage::ObjectiveC: if (isa(nominal) || isa(nominal)) { + // Non-trivial C++ classes and structures are not + // supported by @objc attribute, even though they can + // be represented in Objective-C++. + if (auto *cxxRec = dyn_cast_or_null( + nominal->getClangDecl())) { + if (cxxRec->hasNonTrivialCopyConstructor() || + cxxRec->hasNonTrivialMoveConstructor() || + cxxRec->hasNonTrivialDestructor()) + return failure(); + } + // Optional structs are not representable in (Objective-)C if they // originally came from C, whether or not they are bridged, unless they // came from swift_newtype. If they are defined in Swift, they are only diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index b1fae03d75b0f..97e7ef6796fb0 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -179,6 +179,13 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, // Special diagnostic for structs. if (auto *SD = T->getStructOrBoundGenericStruct()) { + if (isa_and_nonnull(SD->getClangDecl())) { + // This can be a non-trivial C++ record. + diags.diagnose(TypeRange.Start, diag::not_objc_non_trivial_cxx_class) + .highlight(TypeRange) + .limitBehavior(behavior); + return; + } diags.diagnose(TypeRange.Start, diag::not_objc_swift_struct) .highlight(TypeRange) .limitBehavior(behavior); diff --git a/test/Interop/Cxx/objc-correctness/at-objc-api-using-non-trivial-cxx.swift b/test/Interop/Cxx/objc-correctness/at-objc-api-using-non-trivial-cxx.swift new file mode 100644 index 0000000000000..72c2c9a6e459c --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/at-objc-api-using-non-trivial-cxx.swift @@ -0,0 +1,60 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -typecheck -I %t/Inputs -cxx-interoperability-mode=default -verify %t/test.swift + +// REQUIRES: objc_interop + +//--- Inputs/header.h + +class Trivial { +public: + int x; +}; + +class NonTrivial { +public: + NonTrivial(const NonTrivial &other) : x(other.x) {} + ~NonTrivial() { } + +private: + int x; +}; + +struct NonTrivialDestrOnly { + ~NonTrivialDestrOnly() { } + +private: + int x; +}; + +//--- Inputs/module.modulemap + +module NonTrivial { + header "header.h" + export * +} + +//--- test.swift + +import Foundation +import NonTrivial + +@objc +class ObjCObject: NSObject { + @objc var prop: NonTrivial // expected-error {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // expected-note@-1 {{non-trivial C++ classes cannot be represented in Objective-C}} + + @objc var trivProp: Trivial + + override init() { fatalError() } + + @objc func getNonTrivial() -> NonTrivialDestrOnly { // expected-error {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + // expected-note@-1 {{non-trivial C++ classes cannot be represented in Objective-C}} + fatalError() + } + + @objc func getTrivial() -> Trivial { + return Trivial(x: 11) + } +}