Skip to content

Commit 703930f

Browse files
authored
Merge pull request #17998 from mdiep/synthesize-equatable-hashable-for-uninhabited-types-4.2
[4.2] Derive Equatable & Hashable for uninhabited types
2 parents bc7f11c + 28dd1df commit 703930f

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);
@@ -320,6 +316,34 @@ static GuardStmt *returnIfNotEqualGuard(ASTContext &C,
320316
return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body);
321317
}
322318

319+
static void
320+
deriveBodyEquatable_enum_uninhabited_eq(AbstractFunctionDecl *eqDecl) {
321+
auto parentDC = eqDecl->getDeclContext();
322+
ASTContext &C = parentDC->getASTContext();
323+
324+
auto args = eqDecl->getParameterLists().back();
325+
auto aParam = args->get(0);
326+
auto bParam = args->get(1);
327+
328+
assert(!cast<EnumDecl>(aParam->getType()->getAnyNominal())->hasCases());
329+
330+
SmallVector<ASTNode, 1> statements;
331+
SmallVector<ASTNode, 0> cases;
332+
333+
// switch (a, b) { }
334+
auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/ true);
335+
auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/ true);
336+
auto abExpr = TupleExpr::create(C, SourceLoc(), {aRef, bRef}, {}, {},
337+
SourceLoc(), /*HasTrailingClosure*/ false,
338+
/*implicit*/ true);
339+
auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), abExpr,
340+
SourceLoc(), cases, SourceLoc(), C);
341+
statements.push_back(switchStmt);
342+
343+
auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc());
344+
eqDecl->setBody(body);
345+
}
346+
323347
/// Derive the body for an '==' operator for an enum that has no associated
324348
/// values. This generates code that converts each value to its integer ordinal
325349
/// and compares them, which produces an optimal single icmp instruction.
@@ -689,11 +713,13 @@ ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) {
689713

690714
// Build the necessary decl.
691715
if (requirement->getBaseName() == "==") {
692-
if (auto ED = dyn_cast<EnumDecl>(Nominal)) {
716+
if (auto ed = dyn_cast<EnumDecl>(Nominal)) {
693717
auto bodySynthesizer =
694-
ED->hasOnlyCasesWithoutAssociatedValues()
695-
? &deriveBodyEquatable_enum_noAssociatedValues_eq
696-
: &deriveBodyEquatable_enum_hasAssociatedValues_eq;
718+
!ed->hasCases()
719+
? &deriveBodyEquatable_enum_uninhabited_eq
720+
: ed->hasOnlyCasesWithoutAssociatedValues()
721+
? &deriveBodyEquatable_enum_noAssociatedValues_eq
722+
: &deriveBodyEquatable_enum_hasAssociatedValues_eq;
697723
return deriveEquatable_eq(*this, TC.Context.Id_derived_enum_equals,
698724
bodySynthesizer);
699725
} 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)