Skip to content

Commit b9dfd40

Browse files
authored
Merge pull request #29173 from slavapestov/enum-raw-value-not-equatable-fix
Sema: Fix crash when synthesizing RawRepresentable conformance with non-Equatable raw type
2 parents 6c29939 + 1dd527e commit b9dfd40

File tree

5 files changed

+60
-21
lines changed

5 files changed

+60
-21
lines changed

lib/Sema/DerivedConformanceRawRepresentable.cpp

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -438,14 +438,17 @@ deriveRawRepresentable_init(DerivedConformance &derived) {
438438
return initDecl;
439439
}
440440

441-
static bool canSynthesizeRawRepresentable(DerivedConformance &derived) {
442-
auto enumDecl = cast<EnumDecl>(derived.Nominal);
441+
bool DerivedConformance::canDeriveRawRepresentable(DeclContext *DC,
442+
NominalTypeDecl *type) {
443+
auto enumDecl = dyn_cast<EnumDecl>(type);
444+
if (!enumDecl)
445+
return false;
443446

444447
Type rawType = enumDecl->getRawType();
445448
if (!rawType)
446449
return false;
447-
auto parentDC = cast<DeclContext>(derived.ConformanceDecl);
448-
rawType = parentDC->mapTypeIntoContext(rawType);
450+
451+
rawType = DC->mapTypeIntoContext(rawType);
449452

450453
auto inherited = enumDecl->getInherited();
451454
if (!inherited.empty() && inherited.front().wasValidated() &&
@@ -460,7 +463,7 @@ static bool canSynthesizeRawRepresentable(DerivedConformance &derived) {
460463
if (!equatableProto)
461464
return false;
462465

463-
if (TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl, None)
466+
if (TypeChecker::conformsToProtocol(rawType, equatableProto, DC, None)
464467
.isInvalid())
465468
return false;
466469

@@ -488,12 +491,8 @@ static bool canSynthesizeRawRepresentable(DerivedConformance &derived) {
488491

489492
ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) {
490493

491-
// We can only synthesize RawRepresentable for enums.
492-
if (!isa<EnumDecl>(Nominal))
493-
return nullptr;
494-
495-
// Check other preconditions for synthesized conformance.
496-
if (!canSynthesizeRawRepresentable(*this))
494+
// Check preconditions for synthesized conformance.
495+
if (!canDeriveRawRepresentable(cast<DeclContext>(ConformanceDecl), Nominal))
497496
return nullptr;
498497

499498
if (requirement->getBaseName() == Context.Id_rawValue)
@@ -509,12 +508,8 @@ ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) {
509508

510509
Type DerivedConformance::deriveRawRepresentable(AssociatedTypeDecl *assocType) {
511510

512-
// We can only synthesize RawRepresentable for enums.
513-
if (!isa<EnumDecl>(Nominal))
514-
return nullptr;
515-
516-
// Check other preconditions for synthesized conformance.
517-
if (!canSynthesizeRawRepresentable(*this))
511+
// Check preconditions for synthesized conformance.
512+
if (!canDeriveRawRepresentable(cast<DeclContext>(ConformanceDecl), Nominal))
518513
return nullptr;
519514

520515
if (assocType->getName() == Context.Id_RawValue) {

lib/Sema/DerivedConformances.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC,
7171
// The presence of a raw type is an explicit declaration that
7272
// the compiler should derive a RawRepresentable conformance.
7373
case KnownProtocolKind::RawRepresentable:
74-
return enumDecl->hasRawType();
74+
return canDeriveRawRepresentable(DC, Nominal);
7575

7676
// Enums without associated values can implicitly derive Equatable
7777
// conformance.

lib/Sema/DerivedConformances.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ class DerivedConformance {
109109
/// \returns the derived member, which will also be added to the type.
110110
Type deriveCaseIterable(AssociatedTypeDecl *assocType);
111111

112+
/// Determine if a RawRepresentable requirement can be derived for a type.
113+
///
114+
/// This is implemented for non-empty enums without associated values,
115+
/// that declare a raw type in the inheritance clause.
116+
static bool canDeriveRawRepresentable(DeclContext *DC, NominalTypeDecl *type);
117+
112118
/// Derive a RawRepresentable requirement for an enum, if it has a valid
113119
/// raw type and raw values for all of its cases.
114120
///

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4050,8 +4050,6 @@ static void diagnoseConformanceFailure(Type T,
40504050
// conformance to RawRepresentable was inferred.
40514051
if (auto enumDecl = T->getEnumOrBoundGenericEnum()) {
40524052
if (Proto->isSpecificProtocol(KnownProtocolKind::RawRepresentable) &&
4053-
DerivedConformance::derivesProtocolConformance(DC, enumDecl,
4054-
Proto) &&
40554053
enumDecl->hasRawType() &&
40564054
!enumDecl->getRawType()->is<ErrorType>()) {
40574055

test/Sema/enum_raw_representable.swift

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ class Outer {
7777
// scenario too.
7878
let a: Int = E.a // expected-error {{cannot convert value of type 'Outer.E' to specified type 'Int'}}
7979

80-
enum E : Array<Int> { // expected-error {{raw type 'Array<Int>' is not expressible by a string, integer, or floating-point literal}}
80+
enum E : Array<Int> {
81+
// expected-error@-1 {{raw type 'Array<Int>' is not expressible by a string, integer, or floating-point literal}}
82+
// expected-error@-2 {{'Outer.E' declares raw type 'Array<Int>', but does not conform to RawRepresentable and conformance could not be synthesized}}
8183
case a
8284
}
8385
}
@@ -189,3 +191,41 @@ enum ArrayOfNewEquatable : Array<NotEquatable> { }
189191
// expected-error@-2{{'ArrayOfNewEquatable' declares raw type 'Array<NotEquatable>', but does not conform to RawRepresentable and conformance could not be synthesized}}
190192
// expected-error@-3{{RawRepresentable conformance cannot be synthesized because raw type 'Array<NotEquatable>' is not Equatable}}
191193
// expected-error@-4{{an enum with no cases cannot declare a raw type}}
194+
195+
// rdar://58127114
196+
struct NotEquatableInteger : ExpressibleByIntegerLiteral {
197+
typealias IntegerLiteralType = Int
198+
199+
init(integerLiteral: Int) {}
200+
}
201+
202+
enum NotEquatableRawType1 : NotEquatableInteger {
203+
// expected-error@-1 {{'NotEquatableRawType1' declares raw type 'NotEquatableInteger', but does not conform to RawRepresentable and conformance could not be synthesized}}
204+
// expected-error@-2 {{RawRepresentable conformance cannot be synthesized because raw type 'NotEquatableInteger' is not Equatable}}
205+
case a = 123
206+
}
207+
208+
209+
enum NotEquatableRawType2 : NotEquatableInteger {
210+
// expected-error@-1 {{'NotEquatableRawType2' declares raw type 'NotEquatableInteger', but does not conform to RawRepresentable and conformance could not be synthesized}}
211+
// expected-error@-2 {{RawRepresentable conformance cannot be synthesized because raw type 'NotEquatableInteger' is not Equatable}}
212+
typealias RawValue = NotEquatableInteger
213+
214+
case a = 123
215+
}
216+
217+
struct NotEquatableString : ExpressibleByStringLiteral {
218+
init(stringLiteral: String) {}
219+
}
220+
221+
// FIXME: This could be diagnosed a bit better. The notes are disembodied
222+
enum NotEquatableRawType3: NotEquatableString {
223+
// expected-error@-1 {{RawRepresentable conformance cannot be synthesized because raw type 'NotEquatableString' is not Equatable}}
224+
// expected-error@-2 {{'NotEquatableRawType3' declares raw type 'NotEquatableString', but does not conform to RawRepresentable and conformance could not be synthesized}}
225+
case a
226+
typealias RawValue = NotEquatableString
227+
init?(rawValue: Int) { self = .a }
228+
// expected-note@-1 {{candidate has non-matching type '(rawValue: Int)'}}
229+
var rawValue: Int { 0 }
230+
// expected-note@-1 {{candidate has non-matching type 'Int'}}
231+
}

0 commit comments

Comments
 (0)