From be4fe726f2159ca6edb7a6728d2e0f84ca0fe5e9 Mon Sep 17 00:00:00 2001 From: Michael Forster Date: Wed, 13 May 2020 10:54:34 +0200 Subject: [PATCH 1/4] Classify C++ structs as loadable or address-only C++ structs are only loadable if they are trivially copyable. Resolves SR-12472. --- include/swift/AST/Decl.h | 14 +- lib/AST/Decl.cpp | 1 + lib/ClangImporter/ImportDecl.cpp | 20 ++ lib/SIL/IR/TypeLowering.cpp | 4 + .../Interop/Cxx/class/Inputs/loadable-types.h | 158 +++++++++++++++ .../Interop/Cxx/class/Inputs/module.modulemap | 4 + .../Cxx/class/loadable-types-silgen.swift | 180 ++++++++++++++++++ 7 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 test/Interop/Cxx/class/Inputs/loadable-types.h create mode 100644 test/Interop/Cxx/class/loadable-types-silgen.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ce53e757dd562..288a6f805ad73 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -558,10 +558,12 @@ class alignas(1 << DeclAlignInBits) Decl { IsIncompatibleWithWeakReferences : 1 ); - SWIFT_INLINE_BITFIELD(StructDecl, NominalTypeDecl, 1, + SWIFT_INLINE_BITFIELD(StructDecl, NominalTypeDecl, 1+1, /// True if this struct has storage for fields that aren't accessible in /// Swift. - HasUnreferenceableStorage : 1 + HasUnreferenceableStorage : 1, + /// True if this struct is imported from C++ and not trivially copyable. + IsCxxNotTriviallyCopyable : 1 ); SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+1, @@ -3822,6 +3824,14 @@ class StructDecl final : public NominalTypeDecl { void setHasUnreferenceableStorage(bool v) { Bits.StructDecl.HasUnreferenceableStorage = v; } + + bool isCxxNotTriviallyCopyable() const { + return Bits.StructDecl.IsCxxNotTriviallyCopyable; + } + + void setIsCxxNotTriviallyCopyable(bool v) { + Bits.StructDecl.IsCxxNotTriviallyCopyable = v; + } }; /// This is the base type for AncestryOptions. Each flag describes possible diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b15bc60c1bb44..047b23bbe6234 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4090,6 +4090,7 @@ StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, StructLoc(StructLoc) { Bits.StructDecl.HasUnreferenceableStorage = false; + Bits.StructDecl.IsCxxNotTriviallyCopyable = false; } bool NominalTypeDecl::hasMemberwiseInitializer() const { diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index ad1e40783062d..98dccac4e67fb 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -48,6 +48,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/Basic/CharInfo.h" #include "swift/Basic/Statistic.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Lookup.h" @@ -3468,6 +3469,25 @@ namespace { result->setHasUnreferenceableStorage(hasUnreferenceableStorage); + if (auto cxxRecordDecl = dyn_cast(decl)) { + result->setIsCxxNotTriviallyCopyable( + !cxxRecordDecl->isTriviallyCopyable()); + + for (auto ctor : cxxRecordDecl->ctors()) { + if (ctor->isCopyConstructor() && + (ctor->isDeleted() || ctor->getAccess() != clang::AS_public)) { + result->setIsCxxNotTriviallyCopyable(true); + break; + } + } + + if (auto dtor = cxxRecordDecl->getDestructor()) { + if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) { + result->setIsCxxNotTriviallyCopyable(true); + } + } + } + return result; } diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 60b8274e90b08..576d1c4f75109 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -1446,6 +1446,10 @@ namespace { if (handleResilience(structType, D, properties)) return handleAddressOnly(structType, properties); + if (D->isCxxNotTriviallyCopyable()) { + properties.setAddressOnly(); + } + auto subMap = structType->getContextSubstitutionMap(&TC.M, D); // Classify the type according to its stored properties. diff --git a/test/Interop/Cxx/class/Inputs/loadable-types.h b/test/Interop/Cxx/class/Inputs/loadable-types.h new file mode 100644 index 0000000000000..e560fee04bacd --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/loadable-types.h @@ -0,0 +1,158 @@ +#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_LOADABLE_TYPES_H +#define TEST_INTEROP_CXX_CLASS_INPUTS_LOADABLE_TYPES_H + +struct EmptyStruct {}; + +// Tests for individual special members + +struct StructWithDefaultConstructor { + StructWithDefaultConstructor() {} +}; + +struct StructWithAdditionalConstructor { + StructWithAdditionalConstructor() {} + StructWithAdditionalConstructor(int parameter) {} +}; + +struct StructWithCopyConstructor { + StructWithCopyConstructor(const StructWithCopyConstructor &) {} +}; + +struct StructWithInheritedCopyConstructor : StructWithCopyConstructor {}; + +struct StructWithSubobjectCopyConstructor { + StructWithCopyConstructor subobject; +}; + +struct StructWithDefaultedCopyConstructor { + StructWithDefaultedCopyConstructor( + const StructWithDefaultedCopyConstructor &) = default; +}; + +struct StructWithInheritedDefaultedCopyConstructor + : StructWithDefaultedCopyConstructor {}; + +struct StructWithSubobjectDefaultedCopyConstructor { + StructWithDefaultedCopyConstructor subobject; +}; + +struct StructWithPrivateDefaultedCopyConstructor { +private: + StructWithPrivateDefaultedCopyConstructor( + const StructWithPrivateDefaultedCopyConstructor &) = default; +}; + +struct StructWithInheritedPrivateDefaultedCopyConstructor + : StructWithPrivateDefaultedCopyConstructor {}; + +struct StructWithSubobjectPrivateDefaultedCopyConstructor { + StructWithPrivateDefaultedCopyConstructor subobject; +}; + +struct StructWithMoveConstructor { + StructWithMoveConstructor(StructWithMoveConstructor &&) {} +}; + +struct StructWithInheritedMoveConstructor : StructWithMoveConstructor {}; + +struct StructWithSubobjectMoveConstructor { + StructWithMoveConstructor subobject; +}; + +struct StructWithCopyAssignment { + StructWithCopyAssignment &operator=(const StructWithCopyAssignment &) {} +}; + +struct StructWithInheritedCopyAssignment : StructWithCopyAssignment {}; + +struct StructWithSubobjectCopyAssignment { + StructWithCopyAssignment subobject; +}; + +struct StructWithMoveAssignment { + StructWithMoveAssignment &operator=(StructWithMoveAssignment &&) {} +}; + +struct StructWithInheritedMoveAssignment : StructWithMoveAssignment {}; + +struct StructWithSubobjectMoveAssignment { + StructWithMoveAssignment subobject; +}; + +struct StructWithDestructor { + ~StructWithDestructor(){}; +}; + +struct StructWithInheritedDestructor : StructWithDestructor {}; + +struct StructWithSubobjectDestructor { + StructWithDestructor subobject; +}; + +struct StructWithDefaultedDestructor { + ~StructWithDefaultedDestructor() = default; +}; + +struct StructWithInheritedDefaultedDestructor : StructWithDefaultedDestructor { +}; + +struct StructWithSubobjectDefaultedDestructor { + StructWithDefaultedDestructor subobject; +}; + +struct StructWithPrivateDefaultedDestructor { +private: + ~StructWithPrivateDefaultedDestructor() = default; +}; + +struct StructWithInheritedPrivateDefaultedDestructor + : StructWithPrivateDefaultedDestructor {}; + +struct StructWithSubobjectPrivateDefaultedDestructor { + StructWithPrivateDefaultedDestructor subobject; +}; + +// Tests for common sets of special subobjects + +struct StructTriviallyCopyableMovable { + StructTriviallyCopyableMovable(const StructTriviallyCopyableMovable &) = + default; + StructTriviallyCopyableMovable(StructTriviallyCopyableMovable &&) = default; + StructTriviallyCopyableMovable & + operator=(const StructTriviallyCopyableMovable &) = default; + StructTriviallyCopyableMovable & + operator=(StructTriviallyCopyableMovable &&) = default; + ~StructTriviallyCopyableMovable() = default; +}; + +struct StructNonCopyableTriviallyMovable { + StructNonCopyableTriviallyMovable(const StructNonCopyableTriviallyMovable &) = + delete; + StructNonCopyableTriviallyMovable(StructNonCopyableTriviallyMovable &&) = + default; + StructNonCopyableTriviallyMovable & + operator=(const StructNonCopyableTriviallyMovable &) = delete; + StructNonCopyableTriviallyMovable & + operator=(StructNonCopyableTriviallyMovable &&) = default; + ~StructNonCopyableTriviallyMovable() = default; +}; + +struct StructNonCopyableNonMovable { + StructNonCopyableNonMovable(const StructNonCopyableNonMovable &) = delete; + StructNonCopyableNonMovable(StructNonCopyableNonMovable &&) = default; + StructNonCopyableNonMovable & + operator=(const StructNonCopyableNonMovable &) = delete; + StructNonCopyableNonMovable & + operator=(StructNonCopyableNonMovable &&) = default; + ~StructNonCopyableNonMovable() = default; +}; + +struct StructDeletedDestructor { + StructDeletedDestructor(const StructDeletedDestructor &) = default; + StructDeletedDestructor(StructDeletedDestructor &&) = default; + StructDeletedDestructor &operator=(const StructDeletedDestructor &) = default; + StructDeletedDestructor &operator=(StructDeletedDestructor &&) = default; + ~StructDeletedDestructor() = delete; +}; + +#endif diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index a7cb7a5feabef..cb61582d4b941 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -2,6 +2,10 @@ module AccessSpecifiers { header "access-specifiers.h" } +module LoadableTypes { + header "loadable-types.h" +} + module MemberwiseInitializer { header "memberwise-initializer.h" } diff --git a/test/Interop/Cxx/class/loadable-types-silgen.swift b/test/Interop/Cxx/class/loadable-types-silgen.swift new file mode 100644 index 0000000000000..6d7b090c829ae --- /dev/null +++ b/test/Interop/Cxx/class/loadable-types-silgen.swift @@ -0,0 +1,180 @@ +// RUN: %target-swift-emit-silgen -I %S/Inputs -enable-cxx-interop %s | %FileCheck %s + +// This test checks that we classify C++ types as loadable and address-only +// correctly. + +import LoadableTypes + +// Tests for individual special members + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}EmptyStruct) +func pass(s: EmptyStruct) { + // CHECK: bb0(%0 : $EmptyStruct): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithDefaultConstructor) +func pass(s: StructWithDefaultConstructor) { + // CHECK: bb0(%0 : $StructWithDefaultConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithAdditionalConstructor) +func pass(s: StructWithAdditionalConstructor) { + // CHECK: bb0(%0 : $StructWithAdditionalConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithCopyConstructor) +func pass(s: StructWithCopyConstructor) { + // CHECK: bb0(%0 : $*StructWithCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedCopyConstructor) +func pass(s: StructWithInheritedCopyConstructor) { + // CHECK: bb0(%0 : $*StructWithInheritedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectCopyConstructor) +func pass(s: StructWithSubobjectCopyConstructor) { + // CHECK: bb0(%0 : $*StructWithSubobjectCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithDefaultedCopyConstructor) +func pass(s: StructWithDefaultedCopyConstructor) { + // CHECK: bb0(%0 : $StructWithDefaultedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedDefaultedCopyConstructor) +func pass(s: StructWithInheritedDefaultedCopyConstructor) { + // CHECK: bb0(%0 : $StructWithInheritedDefaultedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectDefaultedCopyConstructor) +func pass(s: StructWithSubobjectDefaultedCopyConstructor) { + // CHECK: bb0(%0 : $StructWithSubobjectDefaultedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithPrivateDefaultedCopyConstructor) +func pass(s: StructWithPrivateDefaultedCopyConstructor) { + // CHECK: bb0(%0 : $*StructWithPrivateDefaultedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedPrivateDefaultedCopyConstructor) +func pass(s: StructWithInheritedPrivateDefaultedCopyConstructor) { + // CHECK: bb0(%0 : $*StructWithInheritedPrivateDefaultedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectPrivateDefaultedCopyConstructor) +func pass(s: StructWithSubobjectPrivateDefaultedCopyConstructor) { + // CHECK: bb0(%0 : $*StructWithSubobjectPrivateDefaultedCopyConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithMoveConstructor) +func pass(s: StructWithMoveConstructor) { + // CHECK: bb0(%0 : $*StructWithMoveConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedMoveConstructor) +func pass(s: StructWithInheritedMoveConstructor) { + // CHECK: bb0(%0 : $*StructWithInheritedMoveConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectMoveConstructor) +func pass(s: StructWithSubobjectMoveConstructor) { + // CHECK: bb0(%0 : $*StructWithSubobjectMoveConstructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithCopyAssignment) +func pass(s: StructWithCopyAssignment) { + // CHECK: bb0(%0 : $*StructWithCopyAssignment): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedCopyAssignment) +func pass(s: StructWithInheritedCopyAssignment) { + // CHECK: bb0(%0 : $*StructWithInheritedCopyAssignment): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectCopyAssignment) +func pass(s: StructWithSubobjectCopyAssignment) { + // CHECK: bb0(%0 : $*StructWithSubobjectCopyAssignment): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithMoveAssignment) +func pass(s: StructWithMoveAssignment) { + // CHECK: bb0(%0 : $*StructWithMoveAssignment): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedMoveAssignment) +func pass(s: StructWithInheritedMoveAssignment) { + // CHECK: bb0(%0 : $*StructWithInheritedMoveAssignment): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectMoveAssignment) +func pass(s: StructWithSubobjectMoveAssignment) { + // CHECK: bb0(%0 : $*StructWithSubobjectMoveAssignment): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithDestructor) +func pass(s: StructWithDestructor) { + // CHECK: bb0(%0 : $*StructWithDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedDestructor) +func pass(s: StructWithInheritedDestructor) { + // CHECK: bb0(%0 : $*StructWithInheritedDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectDestructor) +func pass(s: StructWithSubobjectDestructor) { + // CHECK: bb0(%0 : $*StructWithSubobjectDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithDefaultedDestructor) +func pass(s: StructWithDefaultedDestructor) { + // CHECK: bb0(%0 : $StructWithDefaultedDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedDefaultedDestructor) +func pass(s: StructWithInheritedDefaultedDestructor) { + // CHECK: bb0(%0 : $StructWithInheritedDefaultedDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectDefaultedDestructor) +func pass(s: StructWithSubobjectDefaultedDestructor) { + // CHECK: bb0(%0 : $StructWithSubobjectDefaultedDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithPrivateDefaultedDestructor) +func pass(s: StructWithPrivateDefaultedDestructor) { + // CHECK: bb0(%0 : $*StructWithPrivateDefaultedDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithInheritedPrivateDefaultedDestructor) +func pass(s: StructWithInheritedPrivateDefaultedDestructor) { + // CHECK: bb0(%0 : $*StructWithInheritedPrivateDefaultedDestructor): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructWithSubobjectPrivateDefaultedDestructor) +func pass(s: StructWithSubobjectPrivateDefaultedDestructor) { + // CHECK: bb0(%0 : $*StructWithSubobjectPrivateDefaultedDestructor): +} + +// Tests for common sets of special subobjects + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructTriviallyCopyableMovable) +func pass(s: StructTriviallyCopyableMovable) { + // CHECK: bb0(%0 : $StructTriviallyCopyableMovable): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructNonCopyableTriviallyMovable) +func pass(s: StructNonCopyableTriviallyMovable) { + // CHECK: bb0(%0 : $*StructNonCopyableTriviallyMovable): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructNonCopyableNonMovable) +func pass(s: StructNonCopyableNonMovable) { + // CHECK: bb0(%0 : $*StructNonCopyableNonMovable): +} + +// CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructDeletedDestructor) +func pass(s: StructDeletedDestructor) { + // CHECK: bb0(%0 : $*StructDeletedDestructor): +} From e6f599113890c71856b64e52fb1b0a37e1dc14c8 Mon Sep 17 00:00:00 2001 From: Michael Forster Date: Wed, 13 May 2020 14:20:58 +0200 Subject: [PATCH 2/4] Update test/Interop/Cxx/class/Inputs/loadable-types.h Co-authored-by: Dmitri Gribenko --- test/Interop/Cxx/class/Inputs/loadable-types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Interop/Cxx/class/Inputs/loadable-types.h b/test/Interop/Cxx/class/Inputs/loadable-types.h index e560fee04bacd..e920147976dc8 100644 --- a/test/Interop/Cxx/class/Inputs/loadable-types.h +++ b/test/Interop/Cxx/class/Inputs/loadable-types.h @@ -112,7 +112,7 @@ struct StructWithSubobjectPrivateDefaultedDestructor { StructWithPrivateDefaultedDestructor subobject; }; -// Tests for common sets of special subobjects +// Tests for common sets of special member functions. struct StructTriviallyCopyableMovable { StructTriviallyCopyableMovable(const StructTriviallyCopyableMovable &) = From 233b67c9da68bada0dddb7c554a81ec07b30c2b5 Mon Sep 17 00:00:00 2001 From: Michael Forster Date: Wed, 13 May 2020 14:21:09 +0200 Subject: [PATCH 3/4] Update test/Interop/Cxx/class/Inputs/loadable-types.h Co-authored-by: Dmitri Gribenko --- test/Interop/Cxx/class/Inputs/loadable-types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Interop/Cxx/class/Inputs/loadable-types.h b/test/Interop/Cxx/class/Inputs/loadable-types.h index e920147976dc8..c2698961c4b41 100644 --- a/test/Interop/Cxx/class/Inputs/loadable-types.h +++ b/test/Interop/Cxx/class/Inputs/loadable-types.h @@ -80,7 +80,7 @@ struct StructWithSubobjectMoveAssignment { }; struct StructWithDestructor { - ~StructWithDestructor(){}; + ~StructWithDestructor(){} }; struct StructWithInheritedDestructor : StructWithDestructor {}; From 18680701b85439da7c215ec7d56a26b4eab17e68 Mon Sep 17 00:00:00 2001 From: Michael Forster Date: Wed, 13 May 2020 14:21:18 +0200 Subject: [PATCH 4/4] Update test/Interop/Cxx/class/loadable-types-silgen.swift Co-authored-by: Dmitri Gribenko --- test/Interop/Cxx/class/loadable-types-silgen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Interop/Cxx/class/loadable-types-silgen.swift b/test/Interop/Cxx/class/loadable-types-silgen.swift index 6d7b090c829ae..7611f5d73d747 100644 --- a/test/Interop/Cxx/class/loadable-types-silgen.swift +++ b/test/Interop/Cxx/class/loadable-types-silgen.swift @@ -157,7 +157,7 @@ func pass(s: StructWithSubobjectPrivateDefaultedDestructor) { // CHECK: bb0(%0 : $*StructWithSubobjectPrivateDefaultedDestructor): } -// Tests for common sets of special subobjects +// Tests for common sets of special member functions. // CHECK-LABEL: sil hidden [ossa] @$s4main4pass{{.*[ (]}}StructTriviallyCopyableMovable) func pass(s: StructTriviallyCopyableMovable) {