Skip to content

Commit c7da6e0

Browse files
committed
Sema: Fix crash when synthesizing RawRepresentable conformance with non-Equatable raw type
The error recovery logic around derived conformances is a little bit tricky. Make sure we don't crash if a type explicitly provides a RawValue type witness that is not equatable, but omits the witnesses for init(rawValue:) and the rawValue property. Fixes <rdar://problem/58127114>.
1 parent 2311712 commit c7da6e0

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
@@ -441,14 +441,17 @@ deriveRawRepresentable_init(DerivedConformance &derived) {
441441
return initDecl;
442442
}
443443

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

447450
Type rawType = enumDecl->getRawType();
448451
if (!rawType)
449452
return false;
450-
auto parentDC = cast<DeclContext>(derived.ConformanceDecl);
451-
rawType = parentDC->mapTypeIntoContext(rawType);
453+
454+
rawType = DC->mapTypeIntoContext(rawType);
452455

453456
auto inherited = enumDecl->getInherited();
454457
if (!inherited.empty() && inherited.front().wasValidated() &&
@@ -463,7 +466,7 @@ static bool canSynthesizeRawRepresentable(DerivedConformance &derived) {
463466
if (!equatableProto)
464467
return false;
465468

466-
if (TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl, None)
469+
if (TypeChecker::conformsToProtocol(rawType, equatableProto, DC, None)
467470
.isInvalid())
468471
return false;
469472

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

492495
ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) {
493496

494-
// We can only synthesize RawRepresentable for enums.
495-
if (!isa<EnumDecl>(Nominal))
496-
return nullptr;
497-
498-
// Check other preconditions for synthesized conformance.
499-
if (!canSynthesizeRawRepresentable(*this))
497+
// Check preconditions for synthesized conformance.
498+
if (!canDeriveRawRepresentable(cast<DeclContext>(ConformanceDecl), Nominal))
500499
return nullptr;
501500

502501
if (requirement->getBaseName() == Context.Id_rawValue)
@@ -512,12 +511,8 @@ ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) {
512511

513512
Type DerivedConformance::deriveRawRepresentable(AssociatedTypeDecl *assocType) {
514513

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

523518
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
@@ -4038,8 +4038,6 @@ static void diagnoseConformanceFailure(Type T,
40384038
// conformance to RawRepresentable was inferred.
40394039
if (auto enumDecl = T->getEnumOrBoundGenericEnum()) {
40404040
if (Proto->isSpecificProtocol(KnownProtocolKind::RawRepresentable) &&
4041-
DerivedConformance::derivesProtocolConformance(DC, enumDecl,
4042-
Proto) &&
40434041
enumDecl->hasRawType() &&
40444042
!enumDecl->getRawType()->is<ErrorType>()) {
40454043

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)