Skip to content

Commit 63746dd

Browse files
committed
Package types are non-public and should already be treated as non-resilient
although they can be accessed indirectly. Added tests to cover various use cases incl. enum switch exhaustive stmts and addition of resilient members resulting in indirect access in silgen. Resolves rdar://104617177
1 parent 1470023 commit 63746dd

File tree

4 files changed

+446
-1
lines changed

4 files changed

+446
-1
lines changed

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4779,7 +4779,7 @@ int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) {
47794779
}
47804780

47814781
bool NominalTypeDecl::isFormallyResilient() const {
4782-
// Private and (unversioned) internal types always have a
4782+
// Private, (unversioned) internal, and package types always have a
47834783
// fixed layout.
47844784
if (!getFormalAccessScope(/*useDC=*/nullptr,
47854785
/*treatUsableFromInlineAsPublic=*/true).isPublic())
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend -emit-module %t/Utils.swift \
5+
// RUN: -module-name Utils -swift-version 5 -I %t \
6+
// RUN: -package-name mypkg \
7+
// RUN: -enable-library-evolution \
8+
// RUN: -emit-module -emit-module-path %t/Utils.swiftmodule
9+
10+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t -swift-version 5 -package-name mypkg -verify
11+
12+
// RUN: %target-swift-frontend -emit-sil %t/Client.swift -package-name mypkg -I %t > %t/Client.sil
13+
// RUN: %FileCheck %s < %t/Client.sil
14+
15+
16+
//--- Utils.swift
17+
18+
// Resilient; public. Acessed indirectly.
19+
public struct PublicStruct {
20+
public var data: Int
21+
}
22+
23+
// Non-resilient; non-public. Accessed directly.
24+
package struct PkgStruct {
25+
package var data: Int
26+
}
27+
28+
// Non-resilient but accessed indirectly since generic.
29+
package struct PkgStructGeneric<T> {
30+
package var data: T
31+
}
32+
33+
// Non-resilient but accessed indirectly; member is of a resilient type.
34+
package struct PkgStructWithPublicMember {
35+
package var member: PublicStruct
36+
}
37+
38+
// Non-resilient but accessed indirectly; contains existential.
39+
package struct PkgStructWithPublicExistential {
40+
package var member: any PublicProto
41+
}
42+
43+
// Non-resilient but accessed indirectly; contains existential.
44+
package struct PkgStructWithPkgExistential {
45+
package var member: any PkgProto
46+
}
47+
48+
// Resilient; public. Acessed indirectly.
49+
public protocol PublicProto {
50+
var data: Int { get set }
51+
}
52+
53+
// Non-resilient but acessed indirectly; existential.
54+
package protocol PkgProto {
55+
var data: Int { get set }
56+
}
57+
58+
59+
//--- Client.swift
60+
import Utils
61+
62+
package func f(_ arg: PublicStruct) -> Int {
63+
return arg.data
64+
}
65+
66+
// CHECK: // f(_:)
67+
// CHECK-NEXT: sil @$s6Client1fySi5Utils12PublicStructVF : $@convention(thin) (@in_guaranteed PublicStruct) -> Int {
68+
// CHECK-NEXT: // %0 "arg" // users: %3, %1
69+
// CHECK-NEXT: bb0(%0 : $*PublicStruct):
70+
// CHECK-NEXT: debug_value %0 : $*PublicStruct, let, name "arg", argno 1, expr op_deref // id: %1
71+
// CHECK-NEXT: %2 = alloc_stack $PublicStruct // users: %7, %6, %5, %3
72+
// CHECK-NEXT: copy_addr %0 to [init] %2 : $*PublicStruct // id: %3
73+
// CHECK-NEXT: // function_ref PublicStruct.data.getter
74+
// CHECK-NEXT: %4 = function_ref @$s5Utils12PublicStructV4dataSivg : $@convention(method) (@in_guaranteed PublicStruct) -> Int // user: %5
75+
// CHECK-NEXT: %5 = apply %4(%2) : $@convention(method) (@in_guaranteed PublicStruct) -> Int // user: %8
76+
// CHECK-NEXT: destroy_addr %2 : $*PublicStruct // id: %6
77+
// CHECK-NEXT: dealloc_stack %2 : $*PublicStruct // id: %7
78+
// CHECK-NEXT: return %5 : $Int // id: %8
79+
// CHECK-NEXT: } // end sil function '$s6Client1fySi5Utils12PublicStructVF'
80+
81+
82+
package func g(_ arg: PkgStruct) -> Int {
83+
return arg.data
84+
}
85+
86+
// CHECK: // g(_:)
87+
// CHECK-NEXT: sil @$s6Client1gySi5Utils9PkgStructVF : $@convention(thin) (PkgStruct) -> Int {
88+
// CHECK-NEXT: // %0 "arg" // users: %2, %1
89+
// CHECK-NEXT: bb0(%0 : $PkgStruct):
90+
// CHECK-NEXT: debug_value %0 : $PkgStruct, let, name "arg", argno 1 // id: %1
91+
// CHECK-NEXT: %2 = struct_extract %0 : $PkgStruct, #PkgStruct.data // user: %3
92+
// CHECK-NEXT: return %2 : $Int // id: %3
93+
// CHECK-NEXT: } // end sil function '$s6Client1gySi5Utils9PkgStructVF'
94+
95+
package func m<T>(_ arg: PkgStructGeneric<T>) -> T {
96+
return arg.data
97+
}
98+
99+
// CHECK: // m<A>(_:)
100+
// CHECK-NEXT: sil @$s6Client1myx5Utils16PkgStructGenericVyxGlF : $@convention(thin) <T> (@in_guaranteed PkgStructGeneric<T>) -> @out T {
101+
// CHECK-NEXT: // %0 "$return_value" // user: %4
102+
// CHECK-NEXT: // %1 "arg" // users: %3, %2
103+
// CHECK-NEXT: bb0(%0 : $*T, %1 : $*PkgStructGeneric<T>):
104+
// CHECK-NEXT: debug_value %1 : $*PkgStructGeneric<T>, let, name "arg", argno 1, expr op_deref // id: %2
105+
// CHECK-NEXT: %3 = struct_element_addr %1 : $*PkgStructGeneric<T>, #PkgStructGeneric.data // user: %4
106+
// CHECK-NEXT: copy_addr %3 to [init] %0 : $*T // id: %4
107+
// CHECK-NEXT: %5 = tuple () // user: %6
108+
// CHECK-NEXT: return %5 : $() // id: %6
109+
// CHECK-NEXT: } // end sil function '$s6Client1myx5Utils16PkgStructGenericVyxGlF'
110+
111+
package func n(_ arg: PkgStructWithPublicMember) -> Int {
112+
return arg.member.data
113+
}
114+
115+
// CHECK: // n(_:)
116+
// CHECK-NEXT: sil @$s6Client1nySi5Utils25PkgStructWithPublicMemberVF : $@convention(thin) (@in_guaranteed PkgStructWithPublicMember) -> Int {
117+
// CHECK-NEXT: // %0 "arg" // users: %2, %1
118+
// CHECK-NEXT: bb0(%0 : $*PkgStructWithPublicMember):
119+
// CHECK-NEXT: debug_value %0 : $*PkgStructWithPublicMember, let, name "arg", argno 1, expr op_deref // id: %1
120+
// CHECK-NEXT: %2 = struct_element_addr %0 : $*PkgStructWithPublicMember, #PkgStructWithPublicMember.member // user: %4
121+
// CHECK-NEXT: %3 = alloc_stack $PublicStruct // users: %12, %10, %6, %4
122+
// CHECK-NEXT: copy_addr %2 to [init] %3 : $*PublicStruct // id: %4
123+
// CHECK-NEXT: %5 = alloc_stack $PublicStruct // users: %11, %9, %8, %6
124+
// CHECK-NEXT: copy_addr %3 to [init] %5 : $*PublicStruct // id: %6
125+
// CHECK-NEXT: // function_ref PublicStruct.data.getter
126+
// CHECK-NEXT: %7 = function_ref @$s5Utils12PublicStructV4dataSivg : $@convention(method) (@in_guaranteed PublicStruct) -> Int // user: %8
127+
// CHECK-NEXT: %8 = apply %7(%5) : $@convention(method) (@in_guaranteed PublicStruct) -> Int // user: %13
128+
// CHECK-NEXT: destroy_addr %5 : $*PublicStruct // id: %9
129+
// CHECK-NEXT: destroy_addr %3 : $*PublicStruct // id: %10
130+
// CHECK-NEXT: dealloc_stack %5 : $*PublicStruct // id: %11
131+
// CHECK-NEXT: dealloc_stack %3 : $*PublicStruct // id: %12
132+
// CHECK-NEXT: return %8 : $Int // id: %13
133+
// CHECK-NEXT: } // end sil function '$s6Client1nySi5Utils25PkgStructWithPublicMemberVF'
134+
135+
package func p(_ arg: PkgStructWithPublicExistential) -> any PublicProto {
136+
return arg.member
137+
}
138+
139+
// CHECK: // p(_:)
140+
// CHECK-NEXT: sil @$s6Client1py5Utils11PublicProto_pAC013PkgStructWithC11ExistentialVF : $@convention(thin) (@in_guaranteed PkgStructWithPublicExistential) -> @out any PublicProto {
141+
// CHECK-NEXT: // %0 "$return_value" // user: %4
142+
// CHECK-NEXT: // %1 "arg" // users: %3, %2
143+
// CHECK-NEXT: bb0(%0 : $*any PublicProto, %1 : $*PkgStructWithPublicExistential):
144+
// CHECK-NEXT: debug_value %1 : $*PkgStructWithPublicExistential, let, name "arg", argno 1, expr op_deref // id: %2
145+
// CHECK-NEXT: %3 = struct_element_addr %1 : $*PkgStructWithPublicExistential, #PkgStructWithPublicExistential.member // user: %4
146+
// CHECK-NEXT: copy_addr %3 to [init] %0 : $*any PublicProto // id: %4
147+
// CHECK-NEXT: %5 = tuple () // user: %6
148+
// CHECK-NEXT: return %5 : $() // id: %6
149+
// CHECK-NEXT: } // end sil function '$s6Client1py5Utils11PublicProto_pAC013PkgStructWithC11ExistentialVF'
150+
151+
package func q(_ arg: PkgStructWithPkgExistential) -> any PkgProto {
152+
return arg.member
153+
}
154+
155+
// CHECK: // q(_:)
156+
// CHECK-NEXT: sil @$s6Client1qy5Utils8PkgProto_pAC0c10StructWithC11ExistentialVF : $@convention(thin) (@in_guaranteed PkgStructWithPkgExistential) -> @out any PkgProto {
157+
// CHECK-NEXT: // %0 "$return_value" // user: %4
158+
// CHECK-NEXT: // %1 "arg" // users: %3, %2
159+
// CHECK-NEXT: bb0(%0 : $*any PkgProto, %1 : $*PkgStructWithPkgExistential):
160+
// CHECK-NEXT: debug_value %1 : $*PkgStructWithPkgExistential, let, name "arg", argno 1, expr op_deref // id: %2
161+
// CHECK-NEXT: %3 = struct_element_addr %1 : $*PkgStructWithPkgExistential, #PkgStructWithPkgExistential.member // user: %4
162+
// CHECK-NEXT: copy_addr %3 to [init] %0 : $*any PkgProto // id: %4
163+
// CHECK-NEXT: %5 = tuple () // user: %6
164+
// CHECK-NEXT: return %5 : $() // id: %6
165+
// CHECK-NEXT: } // end sil function '$s6Client1qy5Utils8PkgProto_pAC0c10StructWithC11ExistentialVF'
166+
167+
package func r(_ arg: PublicProto) -> Int {
168+
return arg.data
169+
}
170+
171+
// CHECK: // r(_:)
172+
// CHECK-NEXT: sil @$s6Client1rySi5Utils11PublicProto_pF : $@convention(thin) (@in_guaranteed any PublicProto) -> Int {
173+
// CHECK-NEXT: // %0 "arg" // users: %2, %1
174+
// CHECK-NEXT: bb0(%0 : $*any PublicProto):
175+
// CHECK-NEXT: debug_value %0 : $*any PublicProto, let, name "arg", argno 1, expr op_deref // id: %1
176+
// CHECK-NEXT: %2 = open_existential_addr immutable_access %0 : $*any PublicProto
177+
// CHECK-NEXT: %3 = alloc_stack $@opened
178+
// CHECK-NEXT: copy_addr %2 to [init] %3 : $*@opened
179+
// CHECK-NEXT: %5 = witness_method $@opened
180+
// CHECK-NEXT: %6 = apply %5<@opened
181+
// CHECK-NEXT: destroy_addr %3 : $*@opened
182+
// CHECK-NEXT: dealloc_stack %3 : $*@opened
183+
// CHECK-NEXT: return %6 : $Int
184+
// CHECK-NEXT: } // end sil function '$s6Client1rySi5Utils11PublicProto_pF'
185+
186+
package func s(_ arg: PkgProto) -> Int {
187+
return arg.data
188+
}
189+
// CHECK: // s(_:)
190+
// CHECK-NEXT: sil @$s6Client1sySi5Utils8PkgProto_pF : $@convention(thin) (@in_guaranteed any PkgProto) -> Int {
191+
// CHECK-NEXT: // %0 "arg" // users: %2, %1
192+
// CHECK-NEXT: bb0(%0 : $*any PkgProto):
193+
// CHECK-NEXT: debug_value %0 : $*any PkgProto, let, name "arg", argno 1, expr op_deref // id: %1
194+
// CHECK-NEXT: %2 = open_existential_addr immutable_access %0 : $*any PkgProto to $*@opened
195+
// CHECK-NEXT: %3 = alloc_stack $@opened
196+
// CHECK-NEXT: copy_addr %2 to [init] %3 : $*@opened
197+
// CHECK-NEXT: %5 = witness_method $@opened
198+
// CHECK-NEXT: %6 = apply %5<@opened
199+
// CHECK-NEXT: destroy_addr %3 : $*@opened
200+
// CHECK-NEXT: dealloc_stack %3 : $*@opened
201+
// CHECK-NEXT: return %6 : $Int
202+
// CHECK-NEXT: } // end sil function '$s6Client1sySi5Utils8PkgProto_pF'
203+
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend -emit-module %t/Utils.swift \
5+
// RUN: -module-name Utils -swift-version 5 -I %t \
6+
// RUN: -package-name mypkg \
7+
// RUN: -enable-library-evolution \
8+
// RUN: -emit-module -emit-module-path %t/Utils.swiftmodule
9+
10+
// RUN: %target-swift-frontend -emit-sil %t/Client.swift -package-name mypkg -I %t > %t/Client.sil
11+
// RUN: %FileCheck %s < %t/Client.sil
12+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t -swift-version 5 -package-name mypkg -verify
13+
14+
//--- Utils.swift
15+
16+
// Resilient; public.
17+
// Switch stmt requires @unknown default.
18+
public enum PublicEnum {
19+
case one
20+
case two(Int)
21+
}
22+
23+
// Resilient; public.
24+
public struct PublicStruct {
25+
public var publicVar: Int
26+
}
27+
28+
// Non-resilient; frozen. Accessed directly.
29+
// Switch stmt does not require @unknown default.
30+
@frozen
31+
public enum FrozenPublicEnum {
32+
case one
33+
case two(Int)
34+
}
35+
36+
// Non-resilient; non-public / associated value is also non-resilient (Int is @frozen public).
37+
// Accessed directly.
38+
// Switch stmt does not require @unknown default.
39+
package enum PkgEnum {
40+
case one
41+
case two(Int)
42+
}
43+
44+
// Non-resilient but accessed indirectly since associated value is resilient.
45+
// Passed by address to func as @in_guaranteed in Silgen.
46+
// Switch stmt does not require @unknown default.
47+
package enum PkgEnumWithPublicCase {
48+
case one
49+
case two(PublicStruct)
50+
}
51+
52+
// Non-resilient but accessed indirectly since associated value is resilient (existential).
53+
// Passed by address to func as @in_guaranteed in Silgen.
54+
// Switch stmt does not require @unknown default.
55+
package enum PkgEnumWithExistentialCase {
56+
case one
57+
case two(any StringProtocol)
58+
}
59+
60+
// Resilient since inlinable.
61+
@usableFromInline
62+
package enum UfiPkgEnum {
63+
case one
64+
case two(Int)
65+
}
66+
67+
68+
//--- Client.swift
69+
import Utils
70+
71+
package func f(_ arg: PkgEnum) -> Int {
72+
switch arg { // no-warning
73+
case .one:
74+
return 1
75+
case .two(let val):
76+
return 2 + val
77+
}
78+
}
79+
80+
// CHECK: // f(_:)
81+
// CHECK-NEXT: sil @$s6Client1fySi5Utils7PkgEnumOF : $@convention(thin) (PkgEnum) -> Int {
82+
// CHECK-NEXT: // %0 "arg" // users: %2, %1
83+
// CHECK-NEXT: bb0(%0 : $PkgEnum):
84+
// CHECK-NEXT: debug_value %0 : $PkgEnum, let, name "arg", argno 1 // id: %1
85+
// CHECK-NEXT: switch_enum %0 : $PkgEnum, case #PkgEnum.one!enumelt: bb1, case #PkgEnum.two!enumelt: bb2 // id: %2
86+
87+
package func g1(_ arg: PkgEnumWithPublicCase) -> Int {
88+
switch arg { // no-warning
89+
case .one:
90+
return 1
91+
case .two(let val):
92+
return 2 + val.publicVar
93+
}
94+
}
95+
96+
// CHECK: // g1(_:)
97+
// CHECK-NEXT: sil @$s6Client2g1ySi5Utils21PkgEnumWithPublicCaseOF : $@convention(thin) (@in_guaranteed PkgEnumWithPublicCase) -> Int {
98+
// CHECK-NEXT: // %0 "arg" // users: %3, %1
99+
// CHECK-NEXT: bb0(%0 : $*PkgEnumWithPublicCase):
100+
// CHECK-NEXT: debug_value %0 : $*PkgEnumWithPublicCase, let, name "arg", argno 1, expr op_deref // id: %1
101+
// CHECK-NEXT: %2 = alloc_stack $PkgEnumWithPublicCase // users: %29, %9, %7, %4, %3
102+
// CHECK-NEXT: copy_addr %0 to [init] %2 : $*PkgEnumWithPublicCase // id: %3
103+
// CHECK-NEXT: switch_enum_addr %2 : $*PkgEnumWithPublicCase, case #PkgEnumWithPublicCase.one!enumelt: bb1, case #PkgEnumWithPublicCase.two!enumelt: bb2 // id: %4
104+
105+
package func g2(_ arg: PkgEnumWithExistentialCase) -> any StringProtocol {
106+
switch arg { // no-warning
107+
case .one:
108+
return "1"
109+
case .two(let val):
110+
return val
111+
}
112+
}
113+
114+
// CHECK: // g2(_:)
115+
// CHECK-NEXT: sil @$s6Client2g2ySy_p5Utils26PkgEnumWithExistentialCaseOF : $@convention(thin) (@in_guaranteed PkgEnumWithExistentialCase) -> @out any StringProtocol {
116+
// CHECK-NEXT: // %0 "$return_value" // users: %20, %12
117+
// CHECK-NEXT: // %1 "arg" // users: %4, %2
118+
// CHECK-NEXT: bb0(%0 : $*any StringProtocol, %1 : $*PkgEnumWithExistentialCase):
119+
// CHECK-NEXT: debug_value %1 : $*PkgEnumWithExistentialCase, let, name "arg", argno 1, expr op_deref // id: %2
120+
// CHECK-NEXT: %3 = alloc_stack $PkgEnumWithExistentialCase // users: %23, %16, %14, %5, %4
121+
// CHECK-NEXT: copy_addr %1 to [init] %3 : $*PkgEnumWithExistentialCase // id: %4
122+
// CHECK-NEXT: switch_enum_addr %3 : $*PkgEnumWithExistentialCase, case #PkgEnumWithExistentialCase.one!enumelt: bb1, case #PkgEnumWithExistentialCase.two!enumelt: bb2 // id: %5
123+
124+
125+
@inlinable
126+
package func h(_ arg: UfiPkgEnum) -> Int {
127+
switch arg { // expected-warning {{switch covers known cases, but 'UfiPkgEnum' may have additional unknown values}} {{none}} expected-note {{handle unknown values using "@unknown default"}}
128+
case .one:
129+
return 1
130+
case .two(let val):
131+
return 2 + val
132+
}
133+
}
134+
135+
// CHECK: // h(_:)
136+
// CHECK-NEXT: sil @$s6Client1hySi5Utils10UfiPkgEnumOF : $@convention(thin) (@in_guaranteed UfiPkgEnum) -> Int {
137+
// CHECK-NEXT: // %0 "arg" // users: %3, %1
138+
// CHECK-NEXT: bb0(%0 : $*UfiPkgEnum):
139+
// CHECK-NEXT: debug_value %0 : $*UfiPkgEnum, let, name "arg", argno 1, expr op_deref // id: %1
140+
// CHECK-NEXT: %2 = alloc_stack $UfiPkgEnum // users: %21, %10, %8, %5, %4, %3
141+
// CHECK-NEXT: copy_addr %0 to [init] %2 : $*UfiPkgEnum // id: %3
142+
// CHECK-NEXT: %4 = value_metatype $@thick UfiPkgEnum.Type, %2 : $*UfiPkgEnum // user: %24
143+
// CHECK-NEXT: switch_enum_addr %2 : $*UfiPkgEnum, case #UfiPkgEnum.one!enumelt: bb1, case #UfiPkgEnum.two!enumelt: bb2, default bb3 // id: %5
144+
145+
public func k(_ arg: PublicEnum) -> Int {
146+
switch arg { // expected-warning {{switch covers known cases, but 'PublicEnum' may have additional unknown values}} {{none}} expected-note {{handle unknown values using "@unknown default"}}
147+
case .one:
148+
return 1
149+
case .two(let val):
150+
return 2 + val
151+
}
152+
}
153+
// CHECK: // k(_:)
154+
// CHECK-NEXT: sil @$s6Client1kySi5Utils10PublicEnumOF : $@convention(thin) (@in_guaranteed PublicEnum) -> Int {
155+
// CHECK-NEXT: // %0 "arg" // users: %3, %1
156+
// CHECK-NEXT: bb0(%0 : $*PublicEnum):
157+
// CHECK-NEXT: debug_value %0 : $*PublicEnum, let, name "arg", argno 1, expr op_deref // id: %1
158+
// CHECK-NEXT: %2 = alloc_stack $PublicEnum // users: %21, %10, %8, %5, %4, %3
159+
// CHECK-NEXT: copy_addr %0 to [init] %2 : $*PublicEnum // id: %3
160+
// CHECK-NEXT: %4 = value_metatype $@thick PublicEnum.Type, %2 : $*PublicEnum // user: %24
161+
// CHECK-NEXT: switch_enum_addr %2 : $*PublicEnum, case #PublicEnum.one!enumelt: bb1, case #PublicEnum.two!enumelt: bb2, default bb3 // id: %5
162+
163+
public func m(_ arg: FrozenPublicEnum) -> Int {
164+
switch arg { // no-warning
165+
case .one:
166+
return 1
167+
case .two(let val):
168+
return 2 + val
169+
}
170+
}
171+
172+
// CHECK: // m(_:)
173+
// CHECK-NEXT: sil @$s6Client1mySi5Utils16FrozenPublicEnumOF : $@convention(thin) (FrozenPublicEnum) -> Int {
174+
// CHECK-NEXT: // %0 "arg" // users: %2, %1
175+
// CHECK-NEXT: bb0(%0 : $FrozenPublicEnum):
176+
// CHECK-NEXT: debug_value %0 : $FrozenPublicEnum, let, name "arg", argno 1 // id: %1
177+
// CHECK-NEXT: switch_enum %0 : $FrozenPublicEnum, case #FrozenPublicEnum.one!enumelt: bb1, case #FrozenPublicEnum.two!enumelt: bb2 // id: %2
178+
179+

0 commit comments

Comments
 (0)