Skip to content

Commit ce7bdab

Browse files
authored
Merge pull request swiftlang#36752 from LucianoPAlmeida/SR-9425-enum-eq
[SR-9425][Sema] Use derived conformance as witness when possible for == operator of raw representable enums
2 parents a4e71aa + 5eb893f commit ce7bdab

File tree

5 files changed

+200
-3
lines changed

5 files changed

+200
-3
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3833,6 +3833,30 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
38333833
}
38343834
}
38353835

3836+
// If the given witness matches a generic RawRepresentable function conforming
3837+
// with a given protocol e.g. `func == <T : RawRepresentable>(lhs: T, rhs: T) ->
3838+
// Bool where T.RawValue : Equatable`
3839+
static bool isRawRepresentableGenericFunction(
3840+
ASTContext &ctx, const ValueDecl *witness,
3841+
const NormalProtocolConformance *conformance) {
3842+
auto *fnDecl = dyn_cast<AbstractFunctionDecl>(witness);
3843+
if (!fnDecl || !fnDecl->isStdlibDecl())
3844+
return false;
3845+
3846+
return fnDecl->isGeneric() && fnDecl->getGenericParams()->size() == 1 &&
3847+
fnDecl->getGenericRequirements().size() == 2 &&
3848+
llvm::all_of(
3849+
fnDecl->getGenericRequirements(), [&](Requirement genericReq) {
3850+
if (genericReq.getKind() != RequirementKind::Conformance)
3851+
return false;
3852+
return genericReq.getProtocolDecl() ==
3853+
ctx.getProtocol(
3854+
KnownProtocolKind::RawRepresentable) ||
3855+
genericReq.getProtocolDecl() ==
3856+
conformance->getProtocol();
3857+
});
3858+
}
3859+
38363860
ResolveWitnessResult
38373861
ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
38383862
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
@@ -3881,6 +3905,16 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
38813905
bool considerRenames =
38823906
!canDerive && !requirement->getAttrs().hasAttribute<OptionalAttr>() &&
38833907
!requirement->getAttrs().isUnavailable(getASTContext());
3908+
3909+
auto &ctx = getASTContext();
3910+
bool isEquatableConformance = Conformance->getProtocol() ==
3911+
ctx.getProtocol(KnownProtocolKind::Equatable);
3912+
3913+
auto decl = Conformance->getDeclContext()->getSelfNominalTypeDecl();
3914+
auto *enumDecl = dyn_cast_or_null<EnumDecl>(decl);
3915+
bool isSwiftRawRepresentableEnum =
3916+
enumDecl && enumDecl->hasRawType() && !enumDecl->isObjC();
3917+
38843918
if (findBestWitness(requirement,
38853919
considerRenames ? &ignoringNames : nullptr,
38863920
Conformance,
@@ -3889,6 +3923,17 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
38893923
const auto &best = matches[bestIdx];
38903924
auto witness = best.Witness;
38913925

3926+
if (canDerive && isSwiftRawRepresentableEnum && isEquatableConformance) {
3927+
// For swift enum types that can derive equatable conformance,
3928+
// if the best witness is default generic conditional conforming
3929+
// `func == <T : RawRepresentable>(lhs: T, rhs: T) -> Bool where
3930+
// T.RawValue : Equatable` let's return as missing and derive
3931+
// the conformance since it is possible.
3932+
if (isRawRepresentableGenericFunction(ctx, witness, Conformance)) {
3933+
return ResolveWitnessResult::Missing;
3934+
}
3935+
}
3936+
38923937
// If the name didn't actually line up, complain.
38933938
if (ignoringNames &&
38943939
requirement->getName() != best.Witness->getName() &&
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %target-swift-emit-silgen -module-name main %s -verify | %FileCheck %s
2+
// SR-9425
3+
enum MyState : String {
4+
case closed = "closed"
5+
case opened = "opened"
6+
}
7+
8+
@inline(never)
9+
func check_state(_ state : MyState) -> Int {
10+
// CHECK: function_ref @$s4main7MyStateO21__derived_enum_equalsySbAC_ACtFZ
11+
return state == .opened ? 1 : 0
12+
}
13+
14+
// generic-enum.swift
15+
enum GenericMyState<T> : String {
16+
case closed
17+
case opened
18+
}
19+
20+
@inline(never)
21+
func check_generic_state(_ state : GenericMyState<Int>) -> Int {
22+
// CHECK: function_ref @$s4main14GenericMyStateO21__derived_enum_equalsySbACyxG_AEtFZ
23+
return state == .opened ? 1 : 0
24+
}
25+
26+
// regular-enum.swift
27+
enum Regular {
28+
case closed
29+
case opened
30+
}
31+
32+
@inline(never)
33+
func check_regular(_ state : Regular) -> Int {
34+
// CHECK: function_ref @$s4main7RegularO21__derived_enum_equalsySbAC_ACtFZ
35+
return state == .closed ? 1 : 0
36+
}
37+
38+
// string-enum.swift
39+
enum Alphabet : String {
40+
case A = "A", B = "B", C = "C", D = "D", E = "E", F = "F", G = "G", H = "H", I = "I", J = "J"
41+
}
42+
43+
@inline(never)
44+
func check_alphabet(_ state : Alphabet) -> Int {
45+
// CHECK: function_ref @$s4main8AlphabetO21__derived_enum_equalsySbAC_ACtFZ
46+
return state == .E ? 1 : 0
47+
}
48+
49+
@inline(never)
50+
func compareIt(_ state : Alphabet, _ rhs: Alphabet) -> Bool {
51+
// CHECK: function_ref @$s4main8AlphabetO21__derived_enum_equalsySbAC_ACtFZ
52+
return state == rhs
53+
}
54+
55+
// int-enum.swift
56+
enum AlphabetInt : Int {
57+
case A = 10, B = 100, C = 12, D = 456, E = 1, F = 3, G = 77, H = 2, I = 27, J = 42
58+
}
59+
60+
@inline(never)
61+
func check_alphabet_int(_ state : AlphabetInt) -> Int {
62+
// CHECK: function_ref @$s4main11AlphabetIntO21__derived_enum_equalsySbAC_ACtFZ
63+
return state == .E ? 1 : 0
64+
}
65+
66+
@inline(never)
67+
func compareIt(_ state : AlphabetInt, _ rhs: AlphabetInt) -> Bool {
68+
// CHECK: function_ref @$s4main11AlphabetIntO21__derived_enum_equalsySbAC_ACtFZ
69+
return state == rhs
70+
}

test/SILGen/opaque_ownership.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,33 @@ public struct Int64 : ExpressibleByIntegerLiteral, _ExpressibleByBuiltinIntegerL
150150
}
151151
}
152152

153+
public struct Int : _ExpressibleByBuiltinIntegerLiteral, ExpressibleByIntegerLiteral, Equatable {
154+
var _value: Builtin.Int64
155+
public init() {
156+
self = 0
157+
}
158+
public typealias IntegerLiteralType = Int
159+
public init(_builtinIntegerLiteral x: _MaxBuiltinIntegerType) {
160+
_value = Builtin.s_to_s_checked_trunc_IntLiteral_Int64(x).0
161+
}
162+
163+
public init(integerLiteral value: Int) {
164+
self = value
165+
}
166+
167+
public static func ==(_ lhs: Int, rhs: Int) -> Bool {
168+
return Bool(Builtin.cmp_eq_Int64(lhs._value, rhs._value))
169+
}
170+
}
171+
153172
// Test ownership of multi-case Enum values in the context of to @in thunks.
154173
// ---
155174
// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$ss17FloatingPointSignOSQsSQ2eeoiySbx_xtFZTW :
156175
// CHECK: bb0(%0 : $FloatingPointSign, %1 : $FloatingPointSign, %2 : $@thick FloatingPointSign.Type):
157-
// CHECK: %3 = function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> Bool
158-
// CHECK: %4 = apply %3<FloatingPointSign>(%0, %1) : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> Bool
159-
// CHECK: return %4 : $Bool
176+
// CHECK: %3 = metatype $@thin FloatingPointSign.Type // user: %5
177+
// CHECK: %4 = function_ref @$ss17FloatingPointSignO21__derived_enum_equalsySbAB_ABtFZ : $@convention(method) (FloatingPointSign, FloatingPointSign, @thin FloatingPointSign.Type) -> Bool // user: %5
178+
// CHECK: %5 = apply %4(%0, %1, %3) : $@convention(method) (FloatingPointSign, FloatingPointSign, @thin FloatingPointSign.Type) -> Bool // user: %6
179+
// CHECK: return %5 : $Bool
160180
// CHECK-LABEL: } // end sil function '$ss17FloatingPointSignOSQsSQ2eeoiySbx_xtFZTW'
161181
public enum FloatingPointSign: Int64 {
162182
/// The sign for a positive value.

test/api-digester/Outputs/cake-abi.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,37 @@
541541
"moduleName": "cake",
542542
"fixedbinaryorder": 0
543543
},
544+
{
545+
"kind": "Function",
546+
"name": "__derived_enum_equals",
547+
"printedName": "__derived_enum_equals(_:_:)",
548+
"children": [
549+
{
550+
"kind": "TypeNominal",
551+
"name": "Bool",
552+
"printedName": "Swift.Bool",
553+
"usr": "s:Sb"
554+
},
555+
{
556+
"kind": "TypeNominal",
557+
"name": "Number",
558+
"printedName": "cake.Number",
559+
"usr": "s:4cake6NumberO"
560+
},
561+
{
562+
"kind": "TypeNominal",
563+
"name": "Number",
564+
"printedName": "cake.Number",
565+
"usr": "s:4cake6NumberO"
566+
}
567+
],
568+
"declKind": "Func",
569+
"usr": "s:4cake6NumberO21__derived_enum_equalsySbAC_ACtFZ",
570+
"moduleName": "cake",
571+
"static": true,
572+
"implicit": true,
573+
"funcSelfKind": "NonMutating"
574+
},
544575
{
545576
"kind": "Constructor",
546577
"name": "init",

test/api-digester/Outputs/cake.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,37 @@
544544
"usr": "s:4cake6NumberO3oneyA2CmF",
545545
"moduleName": "cake"
546546
},
547+
{
548+
"kind": "Function",
549+
"name": "__derived_enum_equals",
550+
"printedName": "__derived_enum_equals(_:_:)",
551+
"children": [
552+
{
553+
"kind": "TypeNominal",
554+
"name": "Bool",
555+
"printedName": "Swift.Bool",
556+
"usr": "s:Sb"
557+
},
558+
{
559+
"kind": "TypeNominal",
560+
"name": "Number",
561+
"printedName": "cake.Number",
562+
"usr": "s:4cake6NumberO"
563+
},
564+
{
565+
"kind": "TypeNominal",
566+
"name": "Number",
567+
"printedName": "cake.Number",
568+
"usr": "s:4cake6NumberO"
569+
}
570+
],
571+
"declKind": "Func",
572+
"usr": "s:4cake6NumberO21__derived_enum_equalsySbAC_ACtFZ",
573+
"moduleName": "cake",
574+
"static": true,
575+
"implicit": true,
576+
"funcSelfKind": "NonMutating"
577+
},
547578
{
548579
"kind": "Constructor",
549580
"name": "init",

0 commit comments

Comments
 (0)