Skip to content

Commit 4d3616b

Browse files
committed
[cxx-interop] Windows: unify address-only logic and mark non-trivial loadable C++ types as unavailable
Windows logic for determining address-only type layout for a C++ type is now unified with other platforms. However, this means that on Windows, a C++ type with a custom destructor, but a default copy constructor is now loadable, even though it's non-trivial. Since Swift does not support such type operations at the moment (it can't be yet destroyed), mark such type as unavailable in Swift instead, when building for the Windows target. This fixes the Windows miscompilation related to such types when they were passed indirectly to C++ functions even though they're actually passed directly.
1 parent 77b5409 commit 4d3616b

16 files changed

+104
-48
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,39 +2472,33 @@ namespace {
24722472
}
24732473

24742474
if (cxxRecordDecl) {
2475-
// FIXME: Swift right now uses AddressOnly type layout
2476-
// in a way that conflates C++ types
2477-
// that need to be destroyed or copied explicitly with C++
2478-
// types that have to be passed indirectly, because
2479-
// only AddressOnly types can be copied or destroyed using C++
2480-
// semantics. However, in actuality these two concepts are
2481-
// separate and don't map to one notion of AddressOnly type
2482-
// layout cleanly. We should reserve the use of AddressOnly
2483-
// type layout when types have to use C++ copy/move/destroy
2484-
// operations, but allow AddressOnly types to be passed
2485-
// directly as well. This will help unify the MSVC and
2486-
// Itanium difference here, and will allow us to support
2487-
// trivial_abi C++ types as well.
2488-
auto isNonTrivialForPurposeOfCalls =
2489-
[](const clang::CXXRecordDecl *decl) -> bool {
2490-
return decl->hasNonTrivialCopyConstructor() ||
2491-
decl->hasNonTrivialMoveConstructor() ||
2492-
!decl->hasTrivialDestructor();
2493-
};
2494-
auto isAddressOnlySwiftStruct =
2495-
[&](const clang::CXXRecordDecl *decl) -> bool {
2496-
// MSVC ABI allows non-trivially destroyed C++ types
2497-
// to be passed in register. This is not supported, as such
2498-
// type wouldn't be destroyed in Swift correctly. Therefore,
2499-
// force AddressOnly type layout using the old heuristic.
2500-
// FIXME: Support can pass in registers for MSVC correctly.
2501-
if (Impl.SwiftContext.LangOpts.Target.isWindowsMSVCEnvironment())
2502-
return isNonTrivialForPurposeOfCalls(decl);
2503-
return !decl->canPassInRegisters();
2504-
};
2505-
if (auto structResult = dyn_cast<StructDecl>(result))
2506-
structResult->setIsCxxNonTrivial(
2507-
isAddressOnlySwiftStruct(cxxRecordDecl));
2475+
if (auto structResult = dyn_cast<StructDecl>(result)) {
2476+
// Address-only type is a type that can't be passed in registers.
2477+
// Address-only types are typically non-trivial, however some
2478+
// non-trivial types can be loadable as well (although such types
2479+
// are not yet available in Swift).
2480+
bool isAddressOnly = !cxxRecordDecl->canPassInRegisters();
2481+
// Check if the given type is non-trivial to ensure we can
2482+
// still perform the right copy/move/destroy even if it's
2483+
// not an address-only type.
2484+
auto isNonTrivial = [](const clang::CXXRecordDecl *decl) -> bool {
2485+
return decl->hasNonTrivialCopyConstructor() ||
2486+
decl->hasNonTrivialMoveConstructor() ||
2487+
!decl->hasTrivialDestructor();
2488+
};
2489+
if (!isAddressOnly &&
2490+
Impl.SwiftContext.LangOpts.Target.isWindowsMSVCEnvironment() &&
2491+
isNonTrivial(cxxRecordDecl)) {
2492+
// MSVC ABI allows non-trivially destroyed C++ types
2493+
// to be passed in register. This is not supported, as such
2494+
// type wouldn't be destroyed in Swift correctly. Therefore,
2495+
// mark this type as unavailable.
2496+
// FIXME: Support can pass in registers for MSVC correctly.
2497+
Impl.markUnavailable(result, "non-trivial C++ class with trivial "
2498+
"ABI is not yet available in Swift");
2499+
}
2500+
structResult->setIsCxxNonTrivial(isAddressOnly);
2501+
}
25082502

25092503
for (auto &getterAndSetter : Impl.GetterSetterMap[result]) {
25102504
auto getter = getterAndSetter.second.first;

test/Interop/Cxx/class/Inputs/destructors.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@ struct DummyStruct {};
66
struct __attribute__((swift_attr("import_unsafe")))
77
HasUserProvidedDestructorAndDummy {
88
DummyStruct dummy;
9+
HasUserProvidedDestructorAndDummy(DummyStruct dummy) : dummy(dummy) {}
10+
#if __is_target_os(windows)
11+
// On windows, force this type to be address-only.
12+
HasUserProvidedDestructorAndDummy(const HasUserProvidedDestructorAndDummy &) {}
13+
#endif
914
~HasUserProvidedDestructorAndDummy() {}
1015
};
1116

1217
struct __attribute__((swift_attr("import_unsafe"))) HasUserProvidedDestructor {
1318
int *value;
19+
#if __is_target_os(windows)
20+
// On windows, force this type to be address-only.
21+
HasUserProvidedDestructor() {}
22+
HasUserProvidedDestructor(const HasUserProvidedDestructor &other) {}
23+
#endif
1424
~HasUserProvidedDestructor() { *value = 42; }
1525
};
1626

test/Interop/Cxx/class/Inputs/nodiscard.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
class NoDiscardMultiply {
77
public:
88
NoDiscardMultiply() {}
9+
NoDiscardMultiply(const NoDiscardMultiply &) { }
910
~NoDiscardMultiply() {}
1011

1112
[[nodiscard]] int Multiply(int x, int y) { return x * y; }

test/Interop/Cxx/class/Inputs/protocol-conformance.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct DoesNotConformToProtocol {
1212
struct DummyStruct {};
1313

1414
struct __attribute__((swift_attr("import_unsafe"))) NonTrivial {
15+
NonTrivial(const NonTrivial &other) {}
1516
~NonTrivial() {}
1617
NonTrivial(DummyStruct) {}
1718
NonTrivial() {}
@@ -67,4 +68,4 @@ struct HasOperatorPlusEqual {
6768

6869
using HasOperatorPlusEqualInt = HasOperatorPlusEqual<int>;
6970

70-
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_PROTOCOL_CONFORMANCE_H
71+
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_PROTOCOL_CONFORMANCE_H

test/Interop/Cxx/class/Inputs/type-classification.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ struct StructWithSubobjectMoveAssignment {
8080
};
8181

8282
struct __attribute__((swift_attr("import_unsafe"))) StructWithDestructor {
83+
#if __is_target_os(windows)
84+
// On windows, force this type to be address-only.
85+
StructWithDestructor() {}
86+
StructWithDestructor(const StructWithDestructor &other) {}
87+
#endif
88+
8389
~StructWithDestructor() {}
8490
};
8591

test/Interop/Cxx/class/delete-operator-destructor-ref-irgen.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public:
2424
class Container {
2525
public:
2626
Container() : pointer(new BaseClass) {}
27+
Container(const Container &other) : pointer(new BaseClass) {}
2728
~Container() { delete pointer; }
2829

2930
inline int method() const {

test/Interop/Cxx/class/destructors-correct-abi-irgen.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import Destructors
66
// CHECK: [[H:%.*]] = alloca %TSo33HasUserProvidedDestructorAndDummyV
77
// CHECK: [[CXX_THIS:%.*]] = bitcast %TSo33HasUserProvidedDestructorAndDummyV* [[H]] to %struct.HasUserProvidedDestructorAndDummy*
88
// CHECK: call {{.*}}@{{_ZN33HasUserProvidedDestructorAndDummyD(1|2)Ev|"\?\?1HasUserProvidedDestructorAndDummy@@QEAA@XZ"}}(%struct.HasUserProvidedDestructorAndDummy* [[CXX_THIS]])
9+
910
// CHECK: ret void
1011
public func test() {
1112
let d = DummyStruct()
12-
let h = HasUserProvidedDestructorAndDummy(dummy: d)
13+
let h = HasUserProvidedDestructorAndDummy(d)
1314
}

test/Interop/Cxx/class/inheritance/Inputs/fields.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct DerivedFromOneField : OneField {};
3030

3131
struct __attribute__((swift_attr("import_unsafe"))) NonTrivial {
3232
NonTrivial() {}
33+
NonTrivial(const NonTrivial &) {}
3334
~NonTrivial() {}
3435
};
3536

@@ -45,6 +46,7 @@ struct NonTrivialDerivedWithOneField : NonTrivialHasThreeFields {
4546

4647
struct __attribute__((swift_attr("import_unsafe"))) NonTrivialHasOneField {
4748
NonTrivialHasOneField() {}
49+
NonTrivialHasOneField(const NonTrivialHasOneField &other) : e(other.e) {}
4850
~NonTrivialHasOneField() {}
4951

5052
int e = 5;

test/Interop/Cxx/class/inheritance/Inputs/functions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
struct __attribute__((swift_attr("import_unsafe"))) NonTrivial {
22
NonTrivial() {}
3+
NonTrivial(const NonTrivial &) {}
34
~NonTrivial() {}
45

56
inline const char *inNonTrivial() const

test/Interop/Cxx/exceptions/trap-on-exception-irgen-itanium.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ public:
9292
class ClassWithDestructor {
9393
int m = 0;
9494
public:
95+
#if __is_target_os(windows)
96+
// On windows, force this type to be address-only.
97+
inline ClassWithDestructor() noexcept {}
98+
inline ClassWithDestructor(const ClassWithDestructor &other) noexcept : m(other.m) {}
99+
#endif
95100
inline ~ClassWithDestructor() {
96101
(void)freeFunctionNoThrow(0);
97102
}
@@ -100,6 +105,11 @@ public:
100105
class ClassWithThrowingDestructor {
101106
int m = 0;
102107
public:
108+
#if __is_target_os(windows)
109+
// On windows, force this type to be address-only.
110+
inline ClassWithThrowingDestructor() noexcept {}
111+
inline ClassWithThrowingDestructor(const ClassWithThrowingDestructor &other) noexcept : m(other.m) {}
112+
#endif
103113
inline ~ClassWithThrowingDestructor() noexcept(false) {
104114
throw 2;
105115
}
@@ -139,6 +149,8 @@ struct StructWithDefaultConstructor {
139149

140150

141151
struct NonTrivial {
152+
NonTrivial() noexcept;
153+
NonTrivial(const NonTrivial &other) noexcept;
142154
~NonTrivial() {}
143155
};
144156

test/Interop/Cxx/value-witness-table/Inputs/custom-destructors.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33

44
struct __attribute__((swift_attr("import_unsafe"))) HasUserProvidedDestructor {
55
int *value;
6+
HasUserProvidedDestructor() {}
7+
HasUserProvidedDestructor(int *value) : value(value) {}
8+
#if __is_target_os(windows) && !defined(WIN_TRIVIAL)
9+
// On windows, force this type to be address-only.
10+
HasUserProvidedDestructor(const HasUserProvidedDestructor &other) : value(other.value) {}
11+
#endif
612
~HasUserProvidedDestructor() { *value = 42; }
713
};
814

@@ -71,6 +77,11 @@ struct DummyStruct {};
7177
struct __attribute__((swift_attr("import_unsafe")))
7278
HasUserProvidedDestructorAndDummy {
7379
DummyStruct dummy;
80+
HasUserProvidedDestructorAndDummy(DummyStruct dummy) : dummy(dummy) {}
81+
#if __is_target_os(windows) && !defined(WIN_TRIVIAL)
82+
// On windows, force this type to be address-only.
83+
HasUserProvidedDestructorAndDummy(const HasUserProvidedDestructorAndDummy &other) : dummy(other.dummy) {}
84+
#endif
7485
~HasUserProvidedDestructorAndDummy() {}
7586
};
7687

test/Interop/Cxx/value-witness-table/custom-destructors-non-virtual-irgen.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import CustomDestructor
77

88
protocol InitWithDummy {
9-
init(dummy: DummyStruct)
9+
init(_: DummyStruct)
1010
}
1111

1212
extension HasUserProvidedDestructorAndDummy : InitWithDummy { }
@@ -25,7 +25,7 @@ extension HasUserProvidedDestructorAndDummy : InitWithDummy { }
2525
// CHECK-LABEL: define {{.*}}@{{_ZN33HasUserProvidedDestructorAndDummyD(1|2)Ev|"\?\?1HasUserProvidedDestructorAndDummy@@QEAA@XZ"}}
2626
// CHECK: ret
2727
public func testHasUserProvidedDestructorAndDummy() {
28-
_ = HasUserProvidedDestructorAndDummy(dummy: DummyStruct())
28+
_ = HasUserProvidedDestructorAndDummy(DummyStruct())
2929
}
3030

3131
// CHECK-LABEL: define {{.*}}void @"$s4main26testHasDefaultedDestructoryyF"

test/Interop/Cxx/value-witness-table/custom-destructors-non-virtual.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import StdlibUnittest
99
var CXXDestructorTestSuite = TestSuite("CXXDestructor")
1010

1111
protocol InitWithPtr {
12-
init(value: UnsafeMutablePointer<Int32>!)
12+
init(_: UnsafeMutablePointer<Int32>!)
1313
}
1414

1515
extension HasUserProvidedDestructor : InitWithPtr { }
@@ -26,30 +26,30 @@ extension HasEmptyDestructorAndMemberWithUserDefinedConstructor
2626
func withCxxDestructorSideEffects<T>(_ _: inout T) { }
2727

2828
func createTypeWithUserProvidedDestructor(_ ptr: UnsafeMutablePointer<Int32>) {
29-
var obj = HasUserProvidedDestructor(value: ptr)
29+
var obj = HasUserProvidedDestructor(ptr)
3030
withCxxDestructorSideEffects(&obj)
3131
}
3232

3333
func createTypeWithEmptyDestructorAndMemberWithUserDefinedConstructor(
3434
_ ptr: UnsafeMutablePointer<Int32>
3535
) {
36-
let member = HasUserProvidedDestructor(value: ptr)
36+
let member = HasUserProvidedDestructor(ptr)
3737
var obj = HasEmptyDestructorAndMemberWithUserDefinedConstructor(member: member)
3838
withCxxDestructorSideEffects(&obj)
3939
}
4040

4141
func createTypeWithNonTrivialImplicitDestructor(
4242
_ ptr: UnsafeMutablePointer<Int32>
4343
) {
44-
let member = HasUserProvidedDestructor(value: ptr)
44+
let member = HasUserProvidedDestructor(ptr)
4545
var obj = HasNonTrivialImplicitDestructor(member: member)
4646
withCxxDestructorSideEffects(&obj)
4747
}
4848

4949
func createTypeWithNonTrivialDefaultDestructor(
5050
_ ptr: UnsafeMutablePointer<Int32>
5151
) {
52-
let member = HasUserProvidedDestructor(value: ptr)
52+
let member = HasUserProvidedDestructor(ptr)
5353
var obj = HasNonTrivialDefaultedDestructor(member: member)
5454
withCxxDestructorSideEffects(&obj)
5555
}
@@ -58,15 +58,15 @@ func createTypeWithGeneric<T : InitWithPtr>(
5858
_ ptr: UnsafeMutablePointer<Int32>,
5959
type: T.Type
6060
) {
61-
var obj = T(value: ptr)
61+
var obj = T(ptr)
6262
withCxxDestructorSideEffects(&obj)
6363
}
6464

6565
func createTypeWithProtocol(
6666
_ ptr: UnsafeMutablePointer<Int32>,
6767
type: InitWithPtr.Type
6868
) {
69-
var obj = type.self.init(value: ptr)
69+
var obj = type.self.init(ptr)
7070
withCxxDestructorSideEffects(&obj)
7171
}
7272

@@ -75,7 +75,7 @@ func createTypeWithProtocol(
7575
type: InitWithPtr.Type,
7676
holder: InitWithMember.Type
7777
) {
78-
let member = type.self.init(value: ptr)
78+
let member = type.self.init(ptr)
7979
var obj = holder.self.init(member: member as! HasUserProvidedDestructor)
8080
withCxxDestructorSideEffects(&obj)
8181
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-experimental-cxx-interop -Xcc -DWIN_TRIVIAL
2+
3+
// REQUIRES: OS=windows-msvc
4+
5+
import CustomDestructor
6+
7+
_ = HasUserProvidedDestructor() // expected-error {{non-trivial C++ class with trivial ABI is not yet available in Swift}}
8+
_ = HasEmptyDestructorAndMemberWithUserDefinedConstructor() // expected-error {{non-trivial C++ class with trivial ABI is not yet available in Swift}}
9+
_ = HasNonTrivialImplicitDestructor() // expected-error {{non-trivial C++ class with trivial ABI is not yet available in Swift}}
10+
_ = HasNonTrivialDefaultedDestructor() // expected-error {{non-trivial C++ class with trivial ABI is not yet available in Swift}}

test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ namespace ns {
3737
T x;
3838

3939
NonTrivialTemplate();
40-
NonTrivialTemplate(const NonTrivialTemplate<T> &) = default;
40+
NonTrivialTemplate(const NonTrivialTemplate<T> &other) : x(other.x) {}
4141
NonTrivialTemplate(NonTrivialTemplate<T> &&) = default;
4242
~NonTrivialTemplate() {}
4343
};
@@ -46,6 +46,8 @@ namespace ns {
4646

4747
struct NonTrivialImplicitMove {
4848
NonTrivialTemplate<int> member;
49+
50+
NonTrivialImplicitMove(const NonTrivialImplicitMove &other) : member(other.member) {}
4951
};
5052

5153
#define IMMORTAL_REF \

test/SILOptimizer/Inputs/CXXTypesWithUserProvidedDestructor.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
#define TEST_SIL_OPTIMIZER_CXX_WITH_CUSTOM_DESTRUCTOR_H
33

44
struct HasUserProvidedDestructor {
5-
int x;
5+
int x = 0;
6+
7+
HasUserProvidedDestructor(const HasUserProvidedDestructor &other) : x(other.x) {}
68
~HasUserProvidedDestructor() {}
79
};
810

911
struct Loadable {
10-
int x;
12+
int x = 0;
1113
};
1214

1315
struct HasMemberWithUserProvidedDestructor {
1416
Loadable y;
17+
18+
HasMemberWithUserProvidedDestructor(const HasMemberWithUserProvidedDestructor &other) : y(other.y) {}
1519
~HasMemberWithUserProvidedDestructor() {}
1620
};
1721

0 commit comments

Comments
 (0)