From baf45064bf832c2d7f73e257211db7cf65b77a11 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 9 Oct 2025 12:40:54 -0400 Subject: [PATCH 1/5] Add `Equatable` and `Hashable` conformance to `GUID`. On Windows, `GUID` is a currency type and (along with its various typedefs) is used pervasively Windows offers `IsEqualGUID()` and `UuidHash()` for comparing and hashing them, respectively, but `IsEqualGUID()` is a macro in C mode and `UuidHash()` only provides a 16-bit hash. We should provide conformance to these protocols in the WinSDK overlay to provide equivalent functionality in Swift. --- stdlib/public/Windows/WinSDK.swift | 21 +++++++++++++++++++++ test/stdlib/WinSDK_GUID.swift | 13 ++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/stdlib/public/Windows/WinSDK.swift b/stdlib/public/Windows/WinSDK.swift index cfa0f2fdee6b5..52ec37e73c2f8 100644 --- a/stdlib/public/Windows/WinSDK.swift +++ b/stdlib/public/Windows/WinSDK.swift @@ -319,3 +319,24 @@ func _convertWindowsBoolToBool(_ b: WindowsBool) -> Bool { return b.boolValue } +// GUID + +extension GUID: Equatable, Hashable { + @usableFromInline @_transparent + private var uint128Value: UInt128 { + unsafe withUnsafeBytes(of: self) { buffer in + // GUID is 32-bit-aligned only, so use loadUnaligned(). + unsafe buffer.baseAddress!.loadUnaligned(as: UInt128.self) + } + } + + @_transparent + public static func ==(lhs: Self, rhs: Self) -> Bool { + lhs.uint128Value == rhs.uint128Value + } + + @_transparent + public func hash(into: inout Hasher) { + hasher.combine(uint128Value) + } +} \ No newline at end of file diff --git a/test/stdlib/WinSDK_GUID.swift b/test/stdlib/WinSDK_GUID.swift index 631888157003f..a0daf6c197909 100644 --- a/test/stdlib/WinSDK_GUID.swift +++ b/test/stdlib/WinSDK_GUID.swift @@ -1,4 +1,5 @@ -// RUN: %target-build-swift %s +// RUN: %target-swift-frontend -typecheck -swift-version 6 %s -verify +// REQUIRES: executable_test // REQUIRES: OS=windows-msvc // Make sure that importing WinSDK brings in the GUID type, which is declared in @@ -7,3 +8,13 @@ import WinSDK public func usesGUID(_ x: GUID) {} + +// Make sure equating and hashing GUIDs works. + +let guid: GUID = GUID_NULL +assert(guid == guid) +assert(guid.hashValue == guid.hashValue) + +let guid2: GUID = IID_IUnknown +assert(guid != guid2) +assert(guid.hashValue != guid2.hashValue) // well, probably \ No newline at end of file From 6da649b34e21393d2053508b7370ffa5b884cd61 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 9 Oct 2025 13:02:45 -0400 Subject: [PATCH 2/5] Incorporate feedback --- stdlib/public/Windows/WinSDK.swift | 8 +++++--- test/stdlib/WinSDK_GUID.swift | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/stdlib/public/Windows/WinSDK.swift b/stdlib/public/Windows/WinSDK.swift index 52ec37e73c2f8..6d2e0d316a3db 100644 --- a/stdlib/public/Windows/WinSDK.swift +++ b/stdlib/public/Windows/WinSDK.swift @@ -321,9 +321,9 @@ func _convertWindowsBoolToBool(_ b: WindowsBool) -> Bool { // GUID -extension GUID: Equatable, Hashable { +extension GUID: Equatable { @usableFromInline @_transparent - private var uint128Value: UInt128 { + var uint128Value: UInt128 { unsafe withUnsafeBytes(of: self) { buffer in // GUID is 32-bit-aligned only, so use loadUnaligned(). unsafe buffer.baseAddress!.loadUnaligned(as: UInt128.self) @@ -334,9 +334,11 @@ extension GUID: Equatable, Hashable { public static func ==(lhs: Self, rhs: Self) -> Bool { lhs.uint128Value == rhs.uint128Value } +} +extension GUID: Hashable { @_transparent public func hash(into: inout Hasher) { hasher.combine(uint128Value) } -} \ No newline at end of file +} diff --git a/test/stdlib/WinSDK_GUID.swift b/test/stdlib/WinSDK_GUID.swift index a0daf6c197909..b849220c5dd78 100644 --- a/test/stdlib/WinSDK_GUID.swift +++ b/test/stdlib/WinSDK_GUID.swift @@ -1,4 +1,6 @@ -// RUN: %target-swift-frontend -typecheck -swift-version 6 %s -verify +// RUN: %target-build-swift %s -o %t.exe +// RUN: %target-codesign %t.exe +// RUN: %target-run %t.exe // REQUIRES: executable_test // REQUIRES: OS=windows-msvc @@ -17,4 +19,4 @@ assert(guid.hashValue == guid.hashValue) let guid2: GUID = IID_IUnknown assert(guid != guid2) -assert(guid.hashValue != guid2.hashValue) // well, probably \ No newline at end of file +assert(guid.hashValue != guid2.hashValue) // well, probably From 166d6c77e1059f8588fcadea359ee92858928a53 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 9 Oct 2025 13:15:03 -0400 Subject: [PATCH 3/5] Incorporate more feedback --- stdlib/public/Windows/WinSDK.swift | 6 ++++-- test/stdlib/WinSDK_GUID.swift | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Windows/WinSDK.swift b/stdlib/public/Windows/WinSDK.swift index 6d2e0d316a3db..b07fe3acc6fc8 100644 --- a/stdlib/public/Windows/WinSDK.swift +++ b/stdlib/public/Windows/WinSDK.swift @@ -321,15 +321,17 @@ func _convertWindowsBoolToBool(_ b: WindowsBool) -> Bool { // GUID -extension GUID: Equatable { +extension GUID { @usableFromInline @_transparent - var uint128Value: UInt128 { + internal var uint128Value: UInt128 { unsafe withUnsafeBytes(of: self) { buffer in // GUID is 32-bit-aligned only, so use loadUnaligned(). unsafe buffer.baseAddress!.loadUnaligned(as: UInt128.self) } } +} +extension GUID: Equatable { @_transparent public static func ==(lhs: Self, rhs: Self) -> Bool { lhs.uint128Value == rhs.uint128Value diff --git a/test/stdlib/WinSDK_GUID.swift b/test/stdlib/WinSDK_GUID.swift index b849220c5dd78..d34febf0e7d42 100644 --- a/test/stdlib/WinSDK_GUID.swift +++ b/test/stdlib/WinSDK_GUID.swift @@ -1,4 +1,4 @@ -// RUN: %target-build-swift %s -o %t.exe +// RUN: %target-build-swift %s -o %t.exe // RUN: %target-codesign %t.exe // RUN: %target-run %t.exe // REQUIRES: executable_test From 0d72bf1509cc330323a7bc44dd8730223472ee80 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 9 Oct 2025 13:35:53 -0400 Subject: [PATCH 4/5] Fix typo --- stdlib/public/Windows/WinSDK.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Windows/WinSDK.swift b/stdlib/public/Windows/WinSDK.swift index b07fe3acc6fc8..d3f03b78c604e 100644 --- a/stdlib/public/Windows/WinSDK.swift +++ b/stdlib/public/Windows/WinSDK.swift @@ -340,7 +340,7 @@ extension GUID: Equatable { extension GUID: Hashable { @_transparent - public func hash(into: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(uint128Value) } } From 1a29f36bfd3437e55ca74a18ac3fe876c5a0cebf Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 9 Oct 2025 17:41:06 -0400 Subject: [PATCH 5/5] Make conformances @retroactive --- stdlib/public/Windows/WinSDK.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Windows/WinSDK.swift b/stdlib/public/Windows/WinSDK.swift index d3f03b78c604e..435adbad3c51d 100644 --- a/stdlib/public/Windows/WinSDK.swift +++ b/stdlib/public/Windows/WinSDK.swift @@ -331,14 +331,17 @@ extension GUID { } } -extension GUID: Equatable { +// These conformances are marked @retroactive because the GUID type nominally +// comes from the _GUIDDef clang module rather than the WinSDK clang module. + +extension GUID: @retroactive Equatable { @_transparent public static func ==(lhs: Self, rhs: Self) -> Bool { lhs.uint128Value == rhs.uint128Value } } -extension GUID: Hashable { +extension GUID: @retroactive Hashable { @_transparent public func hash(into hasher: inout Hasher) { hasher.combine(uint128Value)