blob: 146a1573d574aff98ac5a335b61c301a8b42041a [file] [log] [blame]
// RUN: %empty-directory(%t)
// RUN: cp %s %t/main.swift
// RUN: %target-swift-frontend -typecheck -verify -primary-file %t/main.swift %S/Inputs/enum_equatable_hashable_other.swift -verify-ignore-unknown
enum Foo {
case A, B
}
if Foo.A == .B { }
var aHash: Int = Foo.A.hashValue
enum Generic<T> {
case A, B
static func method() -> Int {
// Test synthesis of == without any member lookup being done
if A == B { }
return Generic.A.hashValue
}
}
if Generic<Foo>.A == .B { }
var gaHash: Int = Generic<Foo>.A.hashValue
func localEnum() -> Bool {
enum Local {
case A, B
}
return Local.A == .B
}
enum CustomHashable {
case A, B
var hashValue: Int { return 0 }
}
func ==(x: CustomHashable, y: CustomHashable) -> Bool { // expected-note 4 {{non-matching type}}
return true
}
if CustomHashable.A == .B { }
var custHash: Int = CustomHashable.A.hashValue
// We still synthesize conforming overloads of '==' and 'hashValue' if
// explicit definitions don't satisfy the protocol requirements. Probably
// not what we actually want.
enum InvalidCustomHashable {
case A, B
var hashValue: String { return "" } // expected-note{{previously declared here}}
}
func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String { // expected-note 4 {{non-matching type}}
return ""
}
if InvalidCustomHashable.A == .B { }
var s: String = InvalidCustomHashable.A == .B
s = InvalidCustomHashable.A.hashValue
var i: Int = InvalidCustomHashable.A.hashValue
// Check use of an enum's synthesized members before the enum is actually declared.
struct UseEnumBeforeDeclaration {
let eqValue = EnumToUseBeforeDeclaration.A == .A
let hashValue = EnumToUseBeforeDeclaration.A.hashValue
}
enum EnumToUseBeforeDeclaration {
case A
}
// Check enums from another file in the same module.
if FromOtherFile.A == .A {}
let _: Int = FromOtherFile.A.hashValue
func getFromOtherFile() -> AlsoFromOtherFile { return .A }
if .A == getFromOtherFile() {}
func overloadFromOtherFile() -> YetAnotherFromOtherFile { return .A }
func overloadFromOtherFile() -> Bool { return false }
if .A == overloadFromOtherFile() {}
// Complex enums are not implicitly Equatable or Hashable.
enum Complex {
case A(Int)
case B
}
if Complex.A(1) == .B { } // expected-error{{binary operator '==' cannot be applied to operands of type 'Complex' and '_'}}
// expected-note @-1 {{overloads for '==' exist with these partially matching parameter lists: }}
// Enums with equatable payloads are equatable if they explicitly conform.
enum EnumWithEquatablePayload: Equatable {
case A(Int)
case B(String, Int)
case C
}
if EnumWithEquatablePayload.A(1) == .B("x", 1) { }
if EnumWithEquatablePayload.A(1) == .C { }
if EnumWithEquatablePayload.B("x", 1) == .C { }
// Enums with hashable payloads are hashable if they explicitly conform.
enum EnumWithHashablePayload: Hashable {
case A(Int)
case B(String, Int)
case C
}
_ = EnumWithHashablePayload.A(1).hashValue
_ = EnumWithHashablePayload.B("x", 1).hashValue
_ = EnumWithHashablePayload.C.hashValue
// ...and they should also inherit equatability from Hashable.
if EnumWithHashablePayload.A(1) == .B("x", 1) { }
if EnumWithHashablePayload.A(1) == .C { }
if EnumWithHashablePayload.B("x", 1) == .C { }
// Enums with non-hashable payloads don't derive conformance.
struct NotHashable {}
enum EnumWithNonHashablePayload: Hashable { // expected-error 2 {{does not conform}}
case A(NotHashable)
}
// Enums should be able to derive conformances based on the conformances of
// their generic arguments.
enum GenericHashable<T: Hashable>: Hashable {
case A(T)
case B
}
if GenericHashable<String>.A("a") == .B { }
var genericHashableHash: Int = GenericHashable<String>.A("a").hashValue
// But it should be an error if the generic argument doesn't have the necessary
// constraints to satisfy the conditions for derivation.
enum GenericNotHashable<T: Equatable>: Hashable { // expected-error {{does not conform}}
case A(T)
case B
}
if GenericNotHashable<String>.A("a") == .B { }
var genericNotHashableHash: Int = GenericNotHashable<String>.A("a").hashValue // expected-error {{value of type 'GenericNotHashable<String>' has no member 'hashValue'}}
// An enum with no cases should not derive conformance.
enum NoCases: Hashable {} // expected-error 2 {{does not conform}}
// rdar://19773050
private enum Bar<T> {
case E(Unknown<T>) // expected-error {{use of undeclared type 'Unknown'}}
mutating func value() -> T {
switch self {
// FIXME: Should diagnose here that '.' needs to be inserted, but E has an ErrorType at this point
case E(let x):
return x.value
}
}
}
// Equatable extension -- rdar://20981254
enum Instrument {
case Piano
case Violin
case Guitar
}
extension Instrument : Equatable {}
// Explicit conformance should work too
public enum Medicine {
case Antibiotic
case Antihistamine
}
extension Medicine : Equatable {}
public func ==(lhs: Medicine, rhs: Medicine) -> Bool { // expected-note 3 {{non-matching type}}
return true
}
// No explicit conformance; it could be derived, but we don't support extensions
// yet.
extension Complex : Hashable {} // expected-error 2 {{cannot be automatically synthesized in an extension}}
// No explicit conformance and it cannot be derived.
enum NotExplicitlyHashableAndCannotDerive {
case A(NotHashable)
}
extension NotExplicitlyHashableAndCannotDerive : Hashable {} // expected-error 2 {{does not conform}}
// Verify that conformance (albeit manually implemented) can still be added to
// a type in a different file.
extension OtherFileNonconforming: Hashable {
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool { // expected-note 3 {{non-matching type}}
return true
}
var hashValue: Int { return 0 }
}
// ...but synthesis in a type defined in another file doesn't work yet.
extension YetOtherFileNonconforming: Equatable {} // expected-error {{cannot be automatically synthesized in an extension}}
// Verify that an indirect enum doesn't emit any errors as long as its "leaves"
// are conformant.
enum StringBinaryTree: Hashable {
indirect case tree(StringBinaryTree, StringBinaryTree)
case leaf(String)
}
// Add some generics to make it more complex.
enum BinaryTree<Element: Hashable>: Hashable {
indirect case tree(BinaryTree, BinaryTree)
case leaf(Element)
}
// Verify mutually indirect enums.
enum MutuallyIndirectA: Hashable {
indirect case b(MutuallyIndirectB)
case data(Int)
}
enum MutuallyIndirectB: Hashable {
indirect case a(MutuallyIndirectA)
case data(Int)
}
// Verify that it works if the enum itself is indirect, rather than the cases.
indirect enum TotallyIndirect: Hashable {
case another(TotallyIndirect)
case end(Int)
}
// Check the use of conditional conformances.
enum ArrayOfEquatables : Equatable {
case only([Int])
}
struct NotEquatable { }
enum ArrayOfNotEquatables : Equatable { // expected-error{{type 'ArrayOfNotEquatables' does not conform to protocol 'Equatable'}}
case only([NotEquatable])
}
// FIXME: Remove -verify-ignore-unknown.
// <unknown>:0: error: unexpected error produced: invalid redeclaration of 'hashValue'
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool'
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '<T> (Generic<T>, Generic<T>) -> Bool'
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(InvalidCustomHashable, InvalidCustomHashable) -> Bool'
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(EnumToUseBeforeDeclaration, EnumToUseBeforeDeclaration) -> Bool'