Skip to content

Commit 2256c80

Browse files
committed
Sema: Narrow down the derivation of == for RawRepresentable enums to non-resilient modules
This change was originally introduced in swiftlang#36752. The problem occurs in the following scenario: - New compiler with this change is used to build a dylib and swiftinterface file for module A, which defines a RawRepresentable enum type E. - Module B imports module A and references == on two E instances. - The module B is run against a dylib of module A built with the old compiler. At this point, module B will not link (or run) because the symbol for E.== is not present in the old dylib for A. The only real solution here is to disable this new optimization when building a module for -enable-library-evolution, unfortunately. In the future, we could make the derived E.== symbol @_alwaysEmitIntoClient. However today, synthesized declarations cannot be @_alwaysEmitIntoClient because they do not have source text that can be emitted into the swiftinterface file. One day, we might gain the ability to print Exprs as source text that parses back in, at which point we could allow synthesized declarations to be @_alwaysEmitIntoClient. Fixes rdar://problem/84912735 and rdar://problem/82919247.
1 parent e7120a6 commit 2256c80

File tree

2 files changed

+60
-40
lines changed

2 files changed

+60
-40
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4123,6 +4123,11 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
41234123
}
41244124
}
41254125

4126+
static bool isSwiftRawRepresentableEnum(Type adoptee) {
4127+
auto *enumDecl = dyn_cast<EnumDecl>(adoptee->getAnyNominal());
4128+
return (enumDecl && enumDecl->hasRawType() && !enumDecl->isObjC());
4129+
}
4130+
41264131
// If the given witness matches a generic RawRepresentable function conforming
41274132
// with a given protocol e.g. `func == <T : RawRepresentable>(lhs: T, rhs: T) ->
41284133
// Bool where T.RawValue : Equatable`
@@ -4201,13 +4206,8 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
42014206
!requirement->getAttrs().isUnavailable(getASTContext());
42024207

42034208
auto &ctx = getASTContext();
4204-
bool isEquatableConformance = Conformance->getProtocol() ==
4205-
ctx.getProtocol(KnownProtocolKind::Equatable);
4206-
4207-
auto decl = Conformance->getDeclContext()->getSelfNominalTypeDecl();
4208-
auto *enumDecl = dyn_cast_or_null<EnumDecl>(decl);
4209-
bool isSwiftRawRepresentableEnum =
4210-
enumDecl && enumDecl->hasRawType() && !enumDecl->isObjC();
4209+
bool isEquatableConformance = (Conformance->getProtocol() ==
4210+
ctx.getProtocol(KnownProtocolKind::Equatable));
42114211

42124212
if (findBestWitness(requirement,
42134213
considerRenames ? &ignoringNames : nullptr,
@@ -4217,12 +4217,23 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
42174217
const auto &best = matches[bestIdx];
42184218
auto witness = best.Witness;
42194219

4220-
if (canDerive && isSwiftRawRepresentableEnum && isEquatableConformance) {
4221-
// For swift enum types that can derive equatable conformance,
4222-
// if the best witness is default generic conditional conforming
4223-
// `func == <T : RawRepresentable>(lhs: T, rhs: T) -> Bool where
4224-
// T.RawValue : Equatable` let's return as missing and derive
4225-
// the conformance since it is possible.
4220+
if (canDerive &&
4221+
isEquatableConformance &&
4222+
isSwiftRawRepresentableEnum(Adoptee) &&
4223+
!Conformance->getDeclContext()->getParentModule()->isResilient()) {
4224+
// For swift enum types that can derive an Equatable conformance,
4225+
// if the best witness is the default implementation
4226+
//
4227+
// func == <T : RawRepresentable>(lhs: T, rhs: T) -> Bool
4228+
// where T.RawValue : Equatable
4229+
//
4230+
// let's return as missing and derive the conformance, since it will be
4231+
// more efficient than comparing rawValues.
4232+
//
4233+
// However, we only do this if the module is non-resilient. If it is
4234+
// resilient, this change can break ABI by publishing a synthesized ==
4235+
// declaration that may not exist in versions of the framework built
4236+
// with an older compiler.
42264237
if (isRawRepresentableGenericFunction(ctx, witness, Conformance)) {
42274238
return ResolveWitnessResult::Missing;
42284239
}
Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,79 @@
1-
// RUN: %target-swift-emit-silgen -module-name main %s -verify | %FileCheck %s
1+
// RUN: %target-swift-emit-silgen -module-name main %s -verify | %FileCheck %s --check-prefix=FRAGILE
2+
// RUN: %target-swift-emit-silgen -module-name main %s -verify -enable-library-evolution | %FileCheck %s --check-prefix=RESILIENT
3+
24
// SR-9425
3-
enum MyState : String {
5+
public enum MyState : String {
46
case closed = "closed"
57
case opened = "opened"
68
}
79

8-
@inline(never)
9-
func check_state(_ state : MyState) -> Int {
10-
// CHECK: function_ref @$s4main7MyStateO21__derived_enum_equalsySbAC_ACtFZ
10+
// CHECK-LABEL: sil [ossa] @$s4main11check_stateySiAA7MyStateOF : $@convention(thin) (MyState) -> Int {
11+
public func check_state(_ state : MyState) -> Int {
12+
// FRAGILE: function_ref @$s4main7MyStateO21__derived_enum_equalsySbAC_ACtFZ
13+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
1114
return state == .opened ? 1 : 0
1215
}
1316

1417
// generic-enum.swift
15-
enum GenericMyState<T> : String {
18+
public enum GenericMyState<T> : String {
1619
case closed
1720
case opened
1821
}
1922

20-
@inline(never)
21-
func check_generic_state(_ state : GenericMyState<Int>) -> Int {
22-
// CHECK: function_ref @$s4main14GenericMyStateO21__derived_enum_equalsySbACyxG_AEtFZ
23+
// CHECK-LABEL: sil [ossa] @$s4main19check_generic_stateySiAA14GenericMyStateOySiGF : $@convention(thin) (GenericMyState<Int>) -> Int {
24+
public func check_generic_state(_ state : GenericMyState<Int>) -> Int {
25+
// FRAGILE: function_ref @$s4main14GenericMyStateO21__derived_enum_equalsySbACyxG_AEtFZ
26+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
2327
return state == .opened ? 1 : 0
2428
}
2529

2630
// regular-enum.swift
27-
enum Regular {
31+
public enum Regular {
2832
case closed
2933
case opened
3034
}
3135

32-
@inline(never)
33-
func check_regular(_ state : Regular) -> Int {
34-
// CHECK: function_ref @$s4main7RegularO21__derived_enum_equalsySbAC_ACtFZ
36+
// CHECK-LABEL: sil [ossa] @$s4main13check_regularySiAA7RegularOF : $@convention(thin) (Regular) -> Int {
37+
public func check_regular(_ state : Regular) -> Int {
38+
// FRAGILE: function_ref @$s4main7RegularO21__derived_enum_equalsySbAC_ACtFZ
39+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
3540
return state == .closed ? 1 : 0
3641
}
3742

3843
// string-enum.swift
39-
enum Alphabet : String {
44+
public enum Alphabet : String {
4045
case A = "A", B = "B", C = "C", D = "D", E = "E", F = "F", G = "G", H = "H", I = "I", J = "J"
4146
}
4247

43-
@inline(never)
44-
func check_alphabet(_ state : Alphabet) -> Int {
45-
// CHECK: function_ref @$s4main8AlphabetO21__derived_enum_equalsySbAC_ACtFZ
48+
// CHECK-LABEL: sil [ossa] @$s4main14check_alphabetySiAA8AlphabetOF : $@convention(thin) (Alphabet) -> Int {
49+
public func check_alphabet(_ state : Alphabet) -> Int {
50+
// FRAGILE: function_ref @$s4main8AlphabetO21__derived_enum_equalsySbAC_ACtFZ
51+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
4652
return state == .E ? 1 : 0
4753
}
4854

49-
@inline(never)
50-
func compareIt(_ state : Alphabet, _ rhs: Alphabet) -> Bool {
51-
// CHECK: function_ref @$s4main8AlphabetO21__derived_enum_equalsySbAC_ACtFZ
55+
// CHECK-LABEL: sil [ossa] @$s4main9compareItySbAA8AlphabetO_ADtF : $@convention(thin) (Alphabet, Alphabet) -> Bool {
56+
public func compareIt(_ state : Alphabet, _ rhs: Alphabet) -> Bool {
57+
// FRAGILE: function_ref @$s4main8AlphabetO21__derived_enum_equalsySbAC_ACtFZ
58+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
5259
return state == rhs
5360
}
5461

5562
// int-enum.swift
56-
enum AlphabetInt : Int {
63+
public enum AlphabetInt : Int {
5764
case A = 10, B = 100, C = 12, D = 456, E = 1, F = 3, G = 77, H = 2, I = 27, J = 42
5865
}
5966

60-
@inline(never)
61-
func check_alphabet_int(_ state : AlphabetInt) -> Int {
62-
// CHECK: function_ref @$s4main11AlphabetIntO21__derived_enum_equalsySbAC_ACtFZ
67+
// CHECK-LABEL: sil [ossa] @$s4main18check_alphabet_intySiAA11AlphabetIntOF : $@convention(thin) (AlphabetInt) -> Int {
68+
public func check_alphabet_int(_ state : AlphabetInt) -> Int {
69+
// FRAGILE: function_ref @$s4main11AlphabetIntO21__derived_enum_equalsySbAC_ACtFZ
70+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
6371
return state == .E ? 1 : 0
6472
}
6573

66-
@inline(never)
67-
func compareIt(_ state : AlphabetInt, _ rhs: AlphabetInt) -> Bool {
68-
// CHECK: function_ref @$s4main11AlphabetIntO21__derived_enum_equalsySbAC_ACtFZ
74+
// CHECK-LABEL: sil [ossa] @$s4main9compareItySbAA11AlphabetIntO_ADtF : $@convention(thin) (AlphabetInt, AlphabetInt) -> Bool {
75+
public func compareIt(_ state : AlphabetInt, _ rhs: AlphabetInt) -> Bool {
76+
// FRAGILE: function_ref @$s4main11AlphabetIntO21__derived_enum_equalsySbAC_ACtFZ
77+
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
6978
return state == rhs
7079
}

0 commit comments

Comments
 (0)