Skip to content

Commit 85115dd

Browse files
authored
Merge pull request #17756 from mdiep/synthesize-equatable-hashable-for-uninhabited-types
Derive Equatable & Hashable for uninhabited types
2 parents d82eed4 + 30949c7 commit 85115dd

File tree

2 files changed

+40
-14
lines changed

2 files changed

+40
-14
lines changed

lib/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@ static bool canDeriveConformance(TypeChecker &tc, DeclContext *DC,
9292
ProtocolDecl *protocol) {
9393
// The type must be an enum or a struct.
9494
if (auto enumDecl = dyn_cast<EnumDecl>(target)) {
95-
// The enum must have cases.
96-
if (!enumDecl->hasCases())
97-
return false;
98-
9995
// The cases must not have associated values, or all associated values must
10096
// conform to the protocol.
10197
return allAssociatedValuesConformToProtocol(tc, DC, enumDecl, protocol);
@@ -304,6 +300,34 @@ static GuardStmt *returnIfNotEqualGuard(ASTContext &C,
304300
return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body);
305301
}
306302

303+
static void
304+
deriveBodyEquatable_enum_uninhabited_eq(AbstractFunctionDecl *eqDecl) {
305+
auto parentDC = eqDecl->getDeclContext();
306+
ASTContext &C = parentDC->getASTContext();
307+
308+
auto args = eqDecl->getParameterLists().back();
309+
auto aParam = args->get(0);
310+
auto bParam = args->get(1);
311+
312+
assert(!cast<EnumDecl>(aParam->getType()->getAnyNominal())->hasCases());
313+
314+
SmallVector<ASTNode, 1> statements;
315+
SmallVector<ASTNode, 0> cases;
316+
317+
// switch (a, b) { }
318+
auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/ true);
319+
auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/ true);
320+
auto abExpr = TupleExpr::create(C, SourceLoc(), {aRef, bRef}, {}, {},
321+
SourceLoc(), /*HasTrailingClosure*/ false,
322+
/*implicit*/ true);
323+
auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), abExpr,
324+
SourceLoc(), cases, SourceLoc(), C);
325+
statements.push_back(switchStmt);
326+
327+
auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc());
328+
eqDecl->setBody(body);
329+
}
330+
307331
/// Derive the body for an '==' operator for an enum that has no associated
308332
/// values. This generates code that converts each value to its integer ordinal
309333
/// and compares them, which produces an optimal single icmp instruction.
@@ -673,11 +697,13 @@ ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) {
673697

674698
// Build the necessary decl.
675699
if (requirement->getBaseName() == "==") {
676-
if (auto ED = dyn_cast<EnumDecl>(Nominal)) {
700+
if (auto ed = dyn_cast<EnumDecl>(Nominal)) {
677701
auto bodySynthesizer =
678-
ED->hasOnlyCasesWithoutAssociatedValues()
679-
? &deriveBodyEquatable_enum_noAssociatedValues_eq
680-
: &deriveBodyEquatable_enum_hasAssociatedValues_eq;
702+
!ed->hasCases()
703+
? &deriveBodyEquatable_enum_uninhabited_eq
704+
: ed->hasOnlyCasesWithoutAssociatedValues()
705+
? &deriveBodyEquatable_enum_noAssociatedValues_eq
706+
: &deriveBodyEquatable_enum_hasAssociatedValues_eq;
681707
return deriveEquatable_eq(*this, TC.Context.Id_derived_enum_equals,
682708
bodySynthesizer);
683709
} else if (isa<StructDecl>(Nominal))

test/Sema/enum_conformance_synthesis.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ enum CustomHashable {
4545

4646
var hashValue: Int { return 0 }
4747
}
48-
func ==(x: CustomHashable, y: CustomHashable) -> Bool { // expected-note 5 {{non-matching type}}
48+
func ==(x: CustomHashable, y: CustomHashable) -> Bool { // expected-note 4 {{non-matching type}}
4949
return true
5050
}
5151

@@ -63,7 +63,7 @@ enum InvalidCustomHashable {
6363

6464
var hashValue: String { return "" } // expected-note{{previously declared here}}
6565
}
66-
func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String { // expected-note 5 {{non-matching type}}
66+
func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String { // expected-note 4 {{non-matching type}}
6767
return ""
6868
}
6969
func invalidCustomHashable() {
@@ -173,8 +173,8 @@ func genericNotHashable() {
173173
GenericNotHashable<String>.A("a").hash(into: &hasher) // expected-error {{value of type 'GenericNotHashable<String>' has no member 'hash'}}
174174
}
175175

176-
// An enum with no cases should not derive conformance.
177-
enum NoCases: Hashable {} // expected-error 2 {{does not conform}}
176+
// An enum with no cases should also derive conformance.
177+
enum NoCases: Hashable {}
178178

179179
// rdar://19773050
180180
private enum Bar<T> {
@@ -213,7 +213,7 @@ public enum Medicine {
213213

214214
extension Medicine : Equatable {}
215215

216-
public func ==(lhs: Medicine, rhs: Medicine) -> Bool { // expected-note 5 {{non-matching type}}
216+
public func ==(lhs: Medicine, rhs: Medicine) -> Bool { // expected-note 4 {{non-matching type}}
217217
return true
218218
}
219219

@@ -236,7 +236,7 @@ extension NotExplicitlyHashableAndCannotDerive : CaseIterable {} // expected-err
236236
// Verify that conformance (albeit manually implemented) can still be added to
237237
// a type in a different file.
238238
extension OtherFileNonconforming: Hashable {
239-
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool { // expected-note 5 {{non-matching type}}
239+
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool { // expected-note 4 {{non-matching type}}
240240
return true
241241
}
242242
var hashValue: Int { return 0 }

0 commit comments

Comments
 (0)