Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
Expand Down
8 changes: 5 additions & 3 deletions stdlib/public/core/Hashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}

Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/core/Hasher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion test/Frontend/dump-parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 5 additions & 3 deletions test/Parse/inverses.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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 {}

Expand Down
6 changes: 3 additions & 3 deletions test/SILGen/synthesized_conformance_enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: }

Expand Down
6 changes: 3 additions & 3 deletions test/SILGen/synthesized_conformance_struct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: }

Expand Down
6 changes: 3 additions & 3 deletions test/Sema/moveonly_illegal_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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'}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
16 changes: 15 additions & 1 deletion test/api-digester/stability-stdlib-abi-without-asserts.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 changes: 75 additions & 0 deletions test/stdlib/NoncopyableHashable.swift
Original file line number Diff line number Diff line change
@@ -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()