// 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 { 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.A == .B { } var gaHash: Int = Generic.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: Hashable { case A(T) case B } if GenericHashable.A("a") == .B { } var genericHashableHash: Int = GenericHashable.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: Hashable { // expected-error {{does not conform}} case A(T) case B } if GenericNotHashable.A("a") == .B { } var genericNotHashableHash: Int = GenericNotHashable.A("a").hashValue // expected-error {{value of type 'GenericNotHashable' 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 { case E(Unknown) // 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: 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. // :0: error: unexpected error produced: invalid redeclaration of 'hashValue' // :0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool' // :0: error: unexpected note produced: candidate has non-matching type ' (Generic, Generic) -> Bool' // :0: error: unexpected note produced: candidate has non-matching type '(InvalidCustomHashable, InvalidCustomHashable) -> Bool' // :0: error: unexpected note produced: candidate has non-matching type '(EnumToUseBeforeDeclaration, EnumToUseBeforeDeclaration) -> Bool'