mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Allow Hashable: ~Copyable (#85748)
Builds on #85746 which covers Equatable.
This commit is contained in:
@@ -37,6 +37,8 @@
|
||||
#include "swift/AST/Expr.h"
|
||||
#include "swift/AST/ForeignErrorConvention.h"
|
||||
#include "swift/AST/GenericEnvironment.h"
|
||||
#include "swift/AST/InFlightSubstitution.h"
|
||||
#include "swift/AST/KnownProtocols.h"
|
||||
#include "swift/AST/ParameterList.h"
|
||||
#include "swift/AST/ProtocolConformance.h"
|
||||
#include "swift/AST/SubstitutionMap.h"
|
||||
@@ -2430,8 +2432,20 @@ RValue SILGenFunction::emitAnyHashableErasure(SILLocation loc,
|
||||
return emitUndefRValue(loc, getASTContext().getAnyHashableType());
|
||||
|
||||
// Construct the substitution for T: Hashable.
|
||||
auto subMap = SubstitutionMap::getProtocolSubstitutions(
|
||||
conformance.getProtocol(), type, conformance);
|
||||
auto subMap = SubstitutionMap::get(convertFn->getGenericSignature(), type,
|
||||
[&](InFlightSubstitution &ifs,
|
||||
Type ty,
|
||||
ProtocolDecl *proto) -> ProtocolConformanceRef {
|
||||
switch (*proto->getKnownProtocolKind()) {
|
||||
case KnownProtocolKind::Hashable:
|
||||
return conformance;
|
||||
case KnownProtocolKind::Copyable:
|
||||
case KnownProtocolKind::Escapable:
|
||||
return lookupConformance(type, proto);
|
||||
default:
|
||||
llvm_unreachable("no other conformances should be involved");
|
||||
}
|
||||
});
|
||||
|
||||
return emitApplyOfLibraryIntrinsic(loc, convertFn, subMap, value, C);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
/// print("New tap detected at (\(nextTap.x), \(nextTap.y)).")
|
||||
/// }
|
||||
/// // Prints "New tap detected at (0, 1).")
|
||||
public protocol Hashable: Equatable {
|
||||
public protocol Hashable: Equatable & ~Copyable {
|
||||
/// The hash value.
|
||||
///
|
||||
/// Hash values are not guaranteed to be equal across different executions of
|
||||
@@ -135,9 +135,10 @@ public protocol Hashable: Equatable {
|
||||
func _rawHashValue(seed: Int) -> Int
|
||||
}
|
||||
|
||||
extension Hashable {
|
||||
extension Hashable where Self: ~Copyable {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
@_preInverseGenerics
|
||||
public func _rawHashValue(seed: Int) -> Int {
|
||||
var hasher = Hasher(_seed: seed)
|
||||
hasher.combine(self)
|
||||
@@ -148,7 +149,8 @@ extension Hashable {
|
||||
// Called by synthesized `hashValue` implementations.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
public func _hashValue<H: Hashable>(for value: H) -> Int {
|
||||
@_preInverseGenerics
|
||||
public func _hashValue<H: Hashable & ~Copyable>(for value: borrowing H) -> Int {
|
||||
return value._rawHashValue(seed: 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -350,7 +350,8 @@ public struct Hasher {
|
||||
/// - Parameter value: A value to add to the hasher.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
public mutating func combine<H: Hashable>(_ value: H) {
|
||||
@_preInverseGenerics
|
||||
public mutating func combine<H: Hashable & ~Copyable>(_ value: borrowing H) {
|
||||
value.hash(into: &self)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ enum TrailingSemi {
|
||||
// CHECK-AST-LABEL: (func_decl{{.*}}"generic(_:)" "<T : Hashable>" interface_type="<T where T : Hashable> (T) -> ()" access=internal captures=(<generic> {{.*}})
|
||||
func generic<T: Hashable>(_: T) {}
|
||||
// CHECK-AST: (pattern_binding_decl
|
||||
// CHECK-AST: (processed_init=declref_expr type="(Int) -> ()" location={{.*}} range={{.*}} decl="main.(file).generic@{{.*}} [with (substitution_map generic_signature=<T where T : Hashable> T -> Int)]" function_ref=unapplied))
|
||||
// CHECK-AST: (processed_init=declref_expr type="(Int) -> ()" location={{.*}} range={{.*}} decl="main.(file).generic@{{.*}} [with (substitution_map generic_signature=<T where T : Copyable, T : Hashable> T -> Int)]" function_ref=unapplied))
|
||||
let _: (Int) -> () = generic
|
||||
|
||||
// Closures should be marked as escaping or not.
|
||||
|
||||
@@ -52,7 +52,9 @@ public struct MoveOnlyS1<T> : ~Copyable { /*deinit {}*/ }
|
||||
public struct MoveOnlyS2<T: Equatable> : ~Copyable { /*deinit {}*/ }
|
||||
public struct MoveOnlyS3<T: ~Copyable> : ~Copyable { /*deinit {}*/ }
|
||||
|
||||
protocol Rope<Element>: Hashable, ~Copyable { // expected-error {{'Self' required to be 'Copyable' but is marked with '~Copyable'}}
|
||||
protocol CopyHashable { }
|
||||
|
||||
protocol Rope<Element>: CopyHashable, ~Copyable { // expected-error {{'Self' required to be 'Copyable' but is marked with '~Copyable'}}
|
||||
associatedtype Element: ~Copyable
|
||||
}
|
||||
|
||||
@@ -102,8 +104,8 @@ typealias Z4 = ~Rope<Int> // expected-error {{type 'Rope<Int>' cannot be suppres
|
||||
typealias Z5 = (~Int) -> Void // expected-error {{type 'Int' cannot be suppressed}}
|
||||
typealias Z6 = ~() -> () // expected-error {{single argument function types require parentheses}}
|
||||
// expected-error@-1 {{type '()' cannot be suppressed}}
|
||||
typealias Z7 = ~(Copyable & Hashable) // expected-error {{type 'Hashable' cannot be suppressed}}
|
||||
typealias Z8 = ~Copyable & Hashable // expected-error {{composition cannot contain '~Copyable' when another member requires 'Copyable'}}
|
||||
typealias Z7 = ~(Copyable & CopyHashable) // expected-error {{type 'CopyHashable' cannot be suppressed}}
|
||||
typealias Z8 = ~Copyable & CopyHashable // expected-error {{composition cannot contain '~Copyable' when another member requires 'Copyable'}}
|
||||
|
||||
struct NotAProtocol {}
|
||||
|
||||
|
||||
@@ -83,9 +83,9 @@ extension NoValues: Codable {}
|
||||
|
||||
// CHECK-LABEL: sil_witness_table hidden <T where T : Hashable> Enum<T>: Hashable module synthesized_conformance_enum {
|
||||
// CHECK-DAG: base_protocol Equatable: <T where T : Equatable> Enum<T>: Equatable module synthesized_conformance_enum
|
||||
// CHECK-DAG: method #Hashable.hashValue!getter: <Self where Self : Hashable> (Self) -> () -> Int : @$s28synthesized_conformance_enum4EnumOyxGSHAASHRzlSH9hashValueSivgTW // protocol witness for Hashable.hashValue.getter in conformance <A> Enum<A>
|
||||
// CHECK-DAG: method #Hashable.hash: <Self where Self : Hashable> (Self) -> (inout Hasher) -> () : @$s28synthesized_conformance_enum4EnumOyxGSHAASHRzlSH4hash4intoys6HasherVz_tFTW // protocol witness for Hashable.hash(into:) in conformance <A> Enum<A>
|
||||
// CHECK-DAG: method #Hashable._rawHashValue: <Self where Self : Hashable> (Self) -> (Int) -> Int : @$s28synthesized_conformance_enum4EnumOyxGSHAASHRzlSH13_rawHashValue4seedS2i_tFTW // protocol witness for Hashable._rawHashValue(seed:) in conformance <A> Enum<A>
|
||||
// CHECK-DAG: method #Hashable.hashValue!getter: <Self where Self : Hashable, Self : ~Copyable> (Self) -> () -> Int : @$s28synthesized_conformance_enum4EnumOyxGSHAASHRzlSH9hashValueSivgTW // protocol witness for Hashable.hashValue.getter in conformance <A> Enum<A>
|
||||
// CHECK-DAG: method #Hashable.hash: <Self where Self : Hashable, Self : ~Copyable> (Self) -> (inout Hasher) -> () : @$s28synthesized_conformance_enum4EnumOyxGSHAASHRzlSH4hash4intoys6HasherVz_tFTW // protocol witness for Hashable.hash(into:) in conformance <A> Enum<A>
|
||||
// CHECK-DAG: method #Hashable._rawHashValue: <Self where Self : Hashable, Self : ~Copyable> (Self) -> (Int) -> Int : @$s28synthesized_conformance_enum4EnumOyxGSHAASHRzlSH13_rawHashValue4seedS2i_tFTW // protocol witness for Hashable._rawHashValue(seed:) in conformance <A> Enum<A>
|
||||
// CHECK-DAG: conditional_conformance (T: Hashable): dependent
|
||||
// CHECK: }
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ extension Struct: Codable where T: Codable {}
|
||||
|
||||
// CHECK-LABEL: sil_witness_table hidden <T where T : Hashable> Struct<T>: Hashable module synthesized_conformance_struct {
|
||||
// CHECK-DAG: base_protocol Equatable: <T where T : Equatable> Struct<T>: Equatable module synthesized_conformance_struct
|
||||
// CHECK-DAG: method #Hashable.hashValue!getter: <Self where Self : Hashable> (Self) -> () -> Int : @$s30synthesized_conformance_struct6StructVyxGSHAASHRzlSH9hashValueSivgTW // protocol witness for Hashable.hashValue.getter in conformance <A> Struct<A>
|
||||
// CHECK-DAG: method #Hashable.hash: <Self where Self : Hashable> (Self) -> (inout Hasher) -> () : @$s30synthesized_conformance_struct6StructVyxGSHAASHRzlSH4hash4intoys6HasherVz_tFTW // protocol witness for Hashable.hash(into:) in conformance <A> Struct<A>
|
||||
// CHECK-DAG: method #Hashable._rawHashValue: <Self where Self : Hashable> (Self) -> (Int) -> Int : @$s30synthesized_conformance_struct6StructVyxGSHAASHRzlSH13_rawHashValue4seedS2i_tFTW // protocol witness for Hashable._rawHashValue(seed:) in conformance <A> Struct<A>
|
||||
// CHECK-DAG: method #Hashable.hashValue!getter: <Self where Self : Hashable, Self : ~Copyable> (Self) -> () -> Int : @$s30synthesized_conformance_struct6StructVyxGSHAASHRzlSH9hashValueSivgTW // protocol witness for Hashable.hashValue.getter in conformance <A> Struct<A>
|
||||
// CHECK-DAG: method #Hashable.hash: <Self where Self : Hashable, Self : ~Copyable> (Self) -> (inout Hasher) -> () : @$s30synthesized_conformance_struct6StructVyxGSHAASHRzlSH4hash4intoys6HasherVz_tFTW // protocol witness for Hashable.hash(into:) in conformance <A> Struct<A>
|
||||
// CHECK-DAG: method #Hashable._rawHashValue: <Self where Self : Hashable, Self : ~Copyable> (Self) -> (Int) -> Int : @$s30synthesized_conformance_struct6StructVyxGSHAASHRzlSH13_rawHashValue4seedS2i_tFTW // protocol witness for Hashable._rawHashValue(seed:) in conformance <A> Struct<A>
|
||||
// CHECK-DAG: conditional_conformance (T: Hashable): dependent
|
||||
// CHECK: }
|
||||
|
||||
|
||||
@@ -52,11 +52,11 @@ func basic_vararg(_ va: MO...) {} // expected-error {{noncopyable type 'MO' cann
|
||||
func illegalTypes<T>(_ t: T) {
|
||||
let _: Array<MO> // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
let _: Maybe<MO> // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
let _: Dictionary<MO, String> // expected-error {{type 'MO' does not conform to protocol 'Hashable'}}
|
||||
let _: Dictionary<MO, String> // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
let _: [MO] // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
let _: [String : MO] // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
let _: [MO : MO] // expected-error {{type 'MO' does not conform to protocol 'Hashable'}}
|
||||
let _: [MO : T] // expected-error {{type 'MO' does not conform to protocol 'Hashable'}}
|
||||
let _: [MO : MO] // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
let _: [MO : T] // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
|
||||
_ = t as! ValBox<MO> // expected-error {{type 'MO' does not conform to protocol 'Copyable'}}
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ Protocol FixedWidthInteger has added inherited protocol Copyable
|
||||
Protocol FixedWidthInteger has added inherited protocol Escapable
|
||||
Protocol FloatingPoint has added inherited protocol Copyable
|
||||
Protocol FloatingPoint has added inherited protocol Escapable
|
||||
Protocol Hashable has added inherited protocol Copyable
|
||||
Protocol Hashable has added inherited protocol Escapable
|
||||
Protocol Identifiable has added inherited protocol Copyable
|
||||
Protocol Identifiable has added inherited protocol Escapable
|
||||
@@ -399,3 +398,10 @@ Func Comparable.>(_:_:) has parameter 1 changing from Default to Shared
|
||||
Func Comparable.>=(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
|
||||
Func Comparable.>=(_:_:) has parameter 0 changing from Default to Shared
|
||||
Func Comparable.>=(_:_:) has parameter 1 changing from Default to Shared
|
||||
|
||||
// Hashable: ~Copyable
|
||||
Protocol Hashable has generic signature change from <Self : Swift.Equatable> to <Self : Swift.Equatable, Self : ~Copyable>
|
||||
Accessor Hashable.hashValue.Get() has generic signature change from <Self where Self : Swift.Hashable> to <Self where Self : Swift.Hashable, Self : ~Copyable>
|
||||
Func Hashable.hash(into:) has generic signature change from <Self where Self : Swift.Hashable> to <Self where Self : Swift.Hashable, Self : ~Copyable>
|
||||
Func Hasher.combine(_:) has generic signature change from <H where H : Swift.Hashable> to <H where H : Swift.Hashable, H : ~Copyable>
|
||||
Func Hasher.combine(_:) has parameter 0 changing from Default to Shared
|
||||
|
||||
@@ -232,7 +232,6 @@ Protocol FixedWidthInteger has added inherited protocol Copyable
|
||||
Protocol FixedWidthInteger has added inherited protocol Escapable
|
||||
Protocol FloatingPoint has added inherited protocol Copyable
|
||||
Protocol FloatingPoint has added inherited protocol Escapable
|
||||
Protocol Hashable has added inherited protocol Copyable
|
||||
Protocol Hashable has added inherited protocol Escapable
|
||||
Protocol Identifiable has added inherited protocol Copyable
|
||||
Protocol Identifiable has added inherited protocol Escapable
|
||||
@@ -911,5 +910,20 @@ Func Comparable.>=(_:_:) has parameter 0 changing from Default to Shared
|
||||
Func Comparable.>=(_:_:) has parameter 1 changing from Default to Shared
|
||||
Func Comparable.>=(_:_:) is now with @_preInverseGenerics
|
||||
|
||||
// Hashable: ~Copyable
|
||||
Protocol Hashable has generic signature change from <Self : Swift.Equatable> to <Self : Swift.Equatable, Self : ~Copyable>
|
||||
Accessor Hashable.hashValue.Get() has generic signature change from <Self where Self : Swift.Hashable> to <Self where Self : Swift.Hashable, Self : ~Copyable>
|
||||
Func Hashable._rawHashValue(seed:) has generic signature change from <Self where Self : Swift.Hashable> to <Self where Self : Swift.Hashable, Self : ~Copyable>
|
||||
Func Hashable._rawHashValue(seed:) has mangled name changing from '(extension in Swift):Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int' to '(extension in Swift):Swift.Hashable< where A: ~Swift.Copyable>._rawHashValue(seed: Swift.Int) -> Swift.Int'
|
||||
Func Hashable._rawHashValue(seed:) is now with @_preInverseGenerics
|
||||
Func Hashable.hash(into:) has generic signature change from <Self where Self : Swift.Hashable> to <Self where Self : Swift.Hashable, Self : ~Copyable>
|
||||
Func Hasher.combine(_:) has generic signature change from <H where H : Swift.Hashable> to <H where H : Swift.Hashable, H : ~Copyable>
|
||||
Func Hasher.combine(_:) has mangled name changing from 'Swift.Hasher.combine<A where A: Swift.Hashable>(A) -> ()' to 'Swift.Hasher.combine<A where A: Swift.Hashable, A: ~Swift.Copyable>(A) -> ()'
|
||||
Func Hasher.combine(_:) has parameter 0 changing from Default to Shared
|
||||
Func Hasher.combine(_:) is now with @_preInverseGenerics
|
||||
Func _hashValue(for:) has generic signature change from <H where H : Swift.Hashable> to <H where H : Swift.Hashable, H : ~Copyable>
|
||||
Func _hashValue(for:) has mangled name changing from 'Swift._hashValue<A where A: Swift.Hashable>(for: A) -> Swift.Int' to 'Swift._hashValue<A where A: Swift.Hashable, A: ~Swift.Copyable>(for: A) -> Swift.Int'
|
||||
Func _hashValue(for:) has parameter 0 changing from Default to Shared
|
||||
Func _hashValue(for:) is now with @_preInverseGenerics
|
||||
|
||||
// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)
|
||||
|
||||
75
test/stdlib/NoncopyableHashable.swift
Normal file
75
test/stdlib/NoncopyableHashable.swift
Normal file
@@ -0,0 +1,75 @@
|
||||
//===--- NoncopyableHashable.swift - tests for Hashable: ~Copyable ----------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// RUN: %target-run-simple-swift
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
let NoncopyableHashableTests = TestSuite("NoncopyableHashable")
|
||||
|
||||
struct Noncopyable<Wrapped: ~Copyable>: ~Copyable {
|
||||
var wrapped: Wrapped
|
||||
}
|
||||
|
||||
extension Noncopyable: Equatable where Wrapped: Equatable & ~Copyable { }
|
||||
|
||||
extension Noncopyable: Hashable where Wrapped: Hashable & ~Copyable { }
|
||||
|
||||
|
||||
extension Hashable where Self: ~Copyable {
|
||||
func sameHash(as other: borrowing Self) -> Bool {
|
||||
self.hashValue == other.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
func differentHash<T: Hashable & ~Copyable>(_ lhs: borrowing T, _ rhs: borrowing T) -> Bool {
|
||||
lhs.hashValue != rhs.hashValue
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
extension InlineArray where Element: Hashable & ~Copyable {
|
||||
func combinedHashes() -> Int {
|
||||
var hasher = Hasher()
|
||||
for i in self.indices {
|
||||
self[i].hash(into: &hasher)
|
||||
}
|
||||
return hasher.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
NoncopyableHashableTests.test("hashing noncopyables") {
|
||||
let a = Noncopyable(wrapped: 1)
|
||||
let b = Noncopyable(wrapped: 2)
|
||||
let c = Noncopyable(wrapped: 1)
|
||||
|
||||
expectTrue(a.sameHash(as: a))
|
||||
expectFalse(a.sameHash(as: b))
|
||||
expectTrue(a.sameHash(as: c))
|
||||
|
||||
expectTrue(differentHash(a,b))
|
||||
expectFalse(differentHash(a,a))
|
||||
expectFalse(differentHash(a,c))
|
||||
|
||||
let nc2 = Noncopyable(wrapped: Noncopyable(wrapped: "1"))
|
||||
expectTrue(nc2.sameHash(as: nc2))
|
||||
expectTrue(differentHash(nc2, .init(wrapped: .init(wrapped: "2"))))
|
||||
|
||||
guard #available(SwiftStdlib 6.2, *) else { return }
|
||||
|
||||
let a1: [_ of _] = [a,b]
|
||||
let d = Noncopyable(wrapped: 2)
|
||||
let a2: [_ of _] = [c,d]
|
||||
expectEqual(a1.combinedHashes(), a2.combinedHashes())
|
||||
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
Reference in New Issue
Block a user