Skip to content

Commit 563c014

Browse files
authored
[cxx-interop] Fix inherited nested types (#84455)
When a derived class inherits a nested type from a base class in C++, we expect that derived type member to be the same as the base nested type. This patch fixes the ClangImporter's base member cloning to make that the case, and also fixes up some test cases (renames "sub-type" to "nested type" because the term "subtype" is overloaded and misleading here). rdar://161137420
1 parent 03a58c2 commit 563c014

File tree

7 files changed

+196
-12
lines changed

7 files changed

+196
-12
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6346,7 +6346,7 @@ static ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
63466346
auto out = new (rawMemory) TypeAliasDecl(
63476347
typeDecl->getLoc(), typeDecl->getLoc(), typeDecl->getName(),
63486348
typeDecl->getLoc(), nullptr, newContext);
6349-
out->setUnderlyingType(typeDecl->getInterfaceType());
6349+
out->setUnderlyingType(typeDecl->getDeclaredInterfaceType());
63506350
out->setAccess(access);
63516351
inheritance.setUnavailableIfNecessary(decl, out);
63526352
return out;

test/Interop/Cxx/class/inheritance/Inputs/module.modulemap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ module ReferenceToDerived {
1616
requires cplusplus
1717
}
1818

19-
module SubTypes {
20-
header "sub-types.h"
19+
module NestedTypes {
20+
header "nested-types.h"
2121
}
2222

2323
module Subscripts {

test/Interop/Cxx/class/inheritance/Inputs/sub-types.h renamed to test/Interop/Cxx/class/inheritance/Inputs/nested-types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ struct Base {
2020
};
2121

2222
struct Derived : Base {};
23+
struct Derived1 : Base {};
24+
struct Derived2 : Derived1 {};

test/Interop/Cxx/class/inheritance/sub-types-module-interface.swift renamed to test/Interop/Cxx/class/inheritance/nested-types-module-interface.swift

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-ide-test -print-module -module-to-print=SubTypes -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=NestedTypes -I %S/Inputs -source-filename=x -cxx-interoperability-mode=default | %FileCheck %s
22

33
// CHECK: struct Base {
44
// CHECK-NEXT: init()
@@ -39,11 +39,29 @@
3939
// CHECK-NEXT: }
4040
// CHECK-NEXT: }
4141

42-
// CHECK-NEXT: struct Derived {
42+
// CHECK: struct Derived {
4343
// CHECK-NEXT: init()
44-
// CHECK-NEXT: typealias EnumClass = Base.EnumClass.Type
45-
// CHECK-NEXT: typealias Enum = Base.Enum.Type
46-
// CHECK-NEXT: typealias Struct = Base.Struct.Type
47-
// CHECK-NEXT: typealias Parent = Base.Parent.Type
48-
// CHECK-NEXT: typealias Union = Base.Union.Type
44+
// CHECK-NEXT: typealias EnumClass = Base.EnumClass
45+
// CHECK-NEXT: typealias Enum = Base.Enum
46+
// CHECK-NEXT: typealias Struct = Base.Struct
47+
// CHECK-NEXT: typealias Parent = Base.Parent
48+
// CHECK-NEXT: typealias Union = Base.Union
49+
// CHECK-NEXT: }
50+
51+
// CHECK: struct Derived1 {
52+
// CHECK-NEXT: init()
53+
// CHECK-NEXT: typealias EnumClass = Base.EnumClass
54+
// CHECK-NEXT: typealias Enum = Base.Enum
55+
// CHECK-NEXT: typealias Struct = Base.Struct
56+
// CHECK-NEXT: typealias Parent = Base.Parent
57+
// CHECK-NEXT: typealias Union = Base.Union
58+
// CHECK-NEXT: }
59+
60+
// CHECK: struct Derived2 {
61+
// CHECK-NEXT: init()
62+
// CHECK-NEXT: typealias EnumClass = Base.EnumClass
63+
// CHECK-NEXT: typealias Enum = Base.Enum
64+
// CHECK-NEXT: typealias Struct = Base.Struct
65+
// CHECK-NEXT: typealias Parent = Base.Parent
66+
// CHECK-NEXT: typealias Union = Base.Union
4967
// CHECK-NEXT: }
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default
2+
3+
// This test ensures that nested type inherited from the base type are
4+
// interchangeable with the base types themselves.
5+
6+
import NestedTypes
7+
8+
func parent(
9+
bc: Base.EnumClass,
10+
be: Base.Enum,
11+
bs: Base.Struct,
12+
bp: Base.Parent,
13+
bpc: Base.Parent.Child,
14+
bu: Base.Union,
15+
) {
16+
let dc: Derived.EnumClass = bc
17+
let de: Derived.Enum = be
18+
let ds: Derived.Struct = bs
19+
let dp: Derived.Parent = bp
20+
let dpc: Derived.Parent.Child = bpc
21+
let du: Derived.Union = bu
22+
23+
let _: Base.EnumClass = dc
24+
let _: Base.Enum = de
25+
let _: Base.Struct = ds
26+
let _: Base.Parent = dp
27+
let _: Base.Parent.Child = dpc
28+
let _: Base.Union = du
29+
}
30+
31+
func grandparent(
32+
bc: Base.EnumClass,
33+
be: Base.Enum,
34+
bs: Base.Struct,
35+
bp: Base.Parent,
36+
bpc: Base.Parent.Child,
37+
bu: Base.Union,
38+
) {
39+
let dc: Derived2.EnumClass = bc
40+
let de: Derived2.Enum = be
41+
let ds: Derived2.Struct = bs
42+
let dp: Derived2.Parent = bp
43+
let dpc: Derived2.Parent.Child = bpc
44+
let du: Derived2.Union = bu
45+
46+
let _: Derived.EnumClass = dc
47+
let _: Derived.Enum = de
48+
let _: Derived.Struct = ds
49+
let _: Derived.Parent = dp
50+
let _: Derived.Parent.Child = dpc
51+
let _: Derived.Union = du
52+
}
53+
54+
func siblings(
55+
dc: Derived.EnumClass,
56+
de: Derived.Enum,
57+
ds: Derived.Struct,
58+
dp: Derived.Parent,
59+
dpc: Derived.Parent.Child,
60+
du: Derived.Union,
61+
) {
62+
let _: Derived1.EnumClass = dc
63+
let _: Derived1.Enum = de
64+
let _: Derived1.Struct = ds
65+
let _: Derived1.Parent = dp
66+
let _: Derived1.Parent.Child = dpc
67+
let _: Derived1.Union = du
68+
}
69+
70+
// Instances created from derived class can be type-annotated with base class
71+
// and vice versa
72+
func assigner() {
73+
let _: Base.EnumClass = Derived.EnumClass.ecb
74+
let _: Base.Enum = Derived.ea // expected-error {{type 'Derived' has no member 'ea'}}
75+
// ^FIXME: nested enums are broken, so inherited nested enums are broken too
76+
let _: Base.Struct = Derived.Struct(sa: 4, sb: 2)
77+
let _: Base.Parent = Derived.Parent()
78+
let _: Base.Parent.Child = Derived.Parent.Child(pca: 42)
79+
let _: Base.Union = Derived.Union(ua: 42)
80+
81+
let _: Derived.EnumClass = Base.EnumClass.ecc
82+
let _: Derived.Enum = Base.ea // expected-error {{type 'Base' has no member 'ea'}}
83+
// ^FIXME: nested enums are broken, so inherited nested enums are broken too
84+
let _: Derived.Struct = Base.Struct(sa: 4, sb: 2)
85+
let _: Derived.Parent = Base.Parent()
86+
let _: Derived.Parent.Child = Base.Parent.Child(pca: 42)
87+
let _: Derived.Union = Base.Union(ua: 42)
88+
}
89+
90+
// Extensions on base type should be "seen" in derived types too, and vice versa
91+
extension Base.Parent {
92+
static func getChild1() -> Child {
93+
return Child(pca: 111)
94+
}
95+
}
96+
97+
extension Derived {
98+
func haveChild1() {
99+
let _: Parent.Child = Parent.getChild1()
100+
}
101+
}
102+
103+
extension Derived.Parent {
104+
static func getChild2() -> Child {
105+
return Child(pca: 111)
106+
}
107+
}
108+
109+
extension Base {
110+
func haveChild2() {
111+
let _: Parent.Child = Parent.getChild2()
112+
}
113+
}

test/Interop/Cxx/class/inheritance/type-aliases-module-interface.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-ide-test -print-module -module-to-print=TypeAliases -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=TypeAliases -I %S/Inputs -source-filename=x -cxx-interoperability-mode=default | %FileCheck %s
22

33
// CHECK: struct Base {
44
// CHECK-NEXT: init()
@@ -11,7 +11,7 @@
1111

1212
// CHECK-NEXT: struct Derived {
1313
// CHECK-NEXT: init()
14-
// CHECK-NEXT: typealias Struct = Base.Struct.Type
14+
// CHECK-NEXT: typealias Struct = Base.Struct
1515
// CHECK-NEXT: typealias T = Int32
1616
// CHECK-NEXT: typealias U = Base.Struct
1717
// CHECK-NEXT: }
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default
2+
3+
// This test ensures that nested type aliases inherited from the base type are
4+
// interchangeable with the base types themselves.
5+
6+
import TypeAliases
7+
8+
extension Derived {
9+
func takesBaseTypes(bs: Base.Struct, bt: Base.T, bu: Base.U) {
10+
let _: Struct = bs
11+
let _: U = bs
12+
13+
let _: T = bt
14+
let _: Int32 = bt
15+
16+
let _: U = bu
17+
let _: Struct = bu
18+
}
19+
20+
func takesDerivedTypes(ds: Struct, dt: T, du: U) {
21+
let _: Base.Struct = ds
22+
let _: Base.U = ds
23+
24+
let _: Base.T = dt
25+
let _: Int32 = dt
26+
27+
let _: Base.U = du
28+
let _: Base.Struct = du
29+
}
30+
}
31+
func takesBaseTypes(bs: Base.Struct, bt: Base.T, bu: Base.U) {
32+
let _: Derived.Struct = bs
33+
let _: Derived.U = bs
34+
35+
let _: Derived.T = bt
36+
let _: Int32 = bt
37+
38+
let _: Derived.U = bu
39+
let _: Derived.Struct = bu
40+
}
41+
42+
func takesDerivedTypes(ds: Derived.Struct, dt: Derived.T, du: Derived.U) {
43+
let _: Base.Struct = ds
44+
let _: Base.U = ds
45+
46+
let _: Base.T = dt
47+
let _: Int32 = dt
48+
49+
let _: Base.U = du
50+
let _: Base.Struct = du
51+
}

0 commit comments

Comments
 (0)