Skip to content

Commit 577015f

Browse files
authored
Merge pull request #39639 from glessard/se-pointer-convenience
SE-0334: Pointer API Usability Improvements
2 parents 4cc21f4 + 758cdae commit 577015f

File tree

5 files changed

+348
-1
lines changed

5 files changed

+348
-1
lines changed

stdlib/public/core/Pointer.swift

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,41 @@ extension _Pointer /*: Equatable */ {
116116
public static func == (lhs: Self, rhs: Self) -> Bool {
117117
return Bool(Builtin.cmp_eq_RawPointer(lhs._rawValue, rhs._rawValue))
118118
}
119+
120+
/// Returns a Boolean value indicating whether two pointers represent
121+
/// the same memory address.
122+
///
123+
/// - Parameters:
124+
/// - lhs: A pointer.
125+
/// - rhs: Another pointer.
126+
/// - Returns: `true` if `lhs` and `rhs` reference the same memory address;
127+
/// otherwise, `false`.
128+
@inlinable
129+
@_alwaysEmitIntoClient
130+
public static func == <Other: _Pointer>(lhs: Self, rhs: Other) -> Bool {
131+
return Bool(Builtin.cmp_eq_RawPointer(lhs._rawValue, rhs._rawValue))
132+
}
133+
134+
/// Returns a Boolean value indicating whether two pointers represent
135+
/// different memory addresses.
136+
///
137+
/// - Parameters:
138+
/// - lhs: A pointer.
139+
/// - rhs: Another pointer.
140+
/// - Returns: `true` if `lhs` and `rhs` reference different memory addresses;
141+
/// otherwise, `false`.
142+
@inlinable
143+
@_alwaysEmitIntoClient
144+
public static func != <Other: _Pointer>(lhs: Self, rhs: Other) -> Bool {
145+
return Bool(Builtin.cmp_ne_RawPointer(lhs._rawValue, rhs._rawValue))
146+
}
119147
}
120148

121149
extension _Pointer /*: Comparable */ {
122150
// - Note: This is an unsigned comparison unlike Strideable's
123151
// implementation.
124152
/// Returns a Boolean value indicating whether the first pointer references
125-
/// an earlier memory location than the second pointer.
153+
/// a memory location earlier than the second pointer references.
126154
///
127155
/// - Parameters:
128156
/// - lhs: A pointer.
@@ -133,6 +161,62 @@ extension _Pointer /*: Comparable */ {
133161
public static func < (lhs: Self, rhs: Self) -> Bool {
134162
return Bool(Builtin.cmp_ult_RawPointer(lhs._rawValue, rhs._rawValue))
135163
}
164+
165+
/// Returns a Boolean value indicating whether the first pointer references
166+
/// a memory location earlier than the second pointer references.
167+
///
168+
/// - Parameters:
169+
/// - lhs: A pointer.
170+
/// - rhs: Another pointer.
171+
/// - Returns: `true` if `lhs` references a memory address
172+
/// earlier than `rhs`; otherwise, `false`.
173+
@inlinable
174+
@_alwaysEmitIntoClient
175+
public static func < <Other: _Pointer>(lhs: Self, rhs: Other) -> Bool {
176+
return Bool(Builtin.cmp_ult_RawPointer(lhs._rawValue, rhs._rawValue))
177+
}
178+
179+
/// Returns a Boolean value indicating whether the first pointer references
180+
/// a memory location earlier than or same as the second pointer references.
181+
///
182+
/// - Parameters:
183+
/// - lhs: A pointer.
184+
/// - rhs: Another pointer.
185+
/// - Returns: `true` if `lhs` references a memory address
186+
/// earlier than or the same as `rhs`; otherwise, `false`.
187+
@inlinable
188+
@_alwaysEmitIntoClient
189+
public static func <= <Other: _Pointer>(lhs: Self, rhs: Other) -> Bool {
190+
return Bool(Builtin.cmp_ule_RawPointer(lhs._rawValue, rhs._rawValue))
191+
}
192+
193+
/// Returns a Boolean value indicating whether the first pointer references
194+
/// a memory location later than the second pointer references.
195+
///
196+
/// - Parameters:
197+
/// - lhs: A pointer.
198+
/// - rhs: Another pointer.
199+
/// - Returns: `true` if `lhs` references a memory address
200+
/// later than `rhs`; otherwise, `false`.
201+
@inlinable
202+
@_alwaysEmitIntoClient
203+
public static func > <Other: _Pointer>(lhs: Self, rhs: Other) -> Bool {
204+
return Bool(Builtin.cmp_ugt_RawPointer(lhs._rawValue, rhs._rawValue))
205+
}
206+
207+
/// Returns a Boolean value indicating whether the first pointer references
208+
/// a memory location later than or same as the second pointer references.
209+
///
210+
/// - Parameters:
211+
/// - lhs: A pointer.
212+
/// - rhs: Another pointer.
213+
/// - Returns: `true` if `lhs` references a memory address
214+
/// later than or the same as `rhs`; otherwise, `false`.
215+
@inlinable
216+
@_alwaysEmitIntoClient
217+
public static func >= <Other: _Pointer>(lhs: Self, rhs: Other) -> Bool {
218+
return Bool(Builtin.cmp_uge_RawPointer(lhs._rawValue, rhs._rawValue))
219+
}
136220
}
137221

138222
extension _Pointer /*: Strideable*/ {

stdlib/public/core/UnsafePointer.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,23 @@ public struct UnsafePointer<Pointee>: _Pointer {
354354
}
355355
}
356356

357+
/// Obtain a pointer to the stored property referred to by a key path.
358+
///
359+
/// If the key path represents a computed property,
360+
/// this function will return `nil`.
361+
///
362+
/// - Parameter property: A `KeyPath` whose `Root` is `Pointee`.
363+
/// - Returns: A pointer to the stored property represented
364+
/// by the key path, or `nil`.
365+
@inlinable
366+
@_alwaysEmitIntoClient
367+
public func pointer<Property>(
368+
to property: KeyPath<Pointee, Property>
369+
) -> UnsafePointer<Property>? {
370+
guard let o = property._storedInlineOffset else { return nil }
371+
return .init(Builtin.gepRaw_Word(_rawValue, o._builtinWordValue))
372+
}
373+
357374
@inlinable // unsafe-performance
358375
internal static var _max: UnsafePointer {
359376
return UnsafePointer(
@@ -1051,6 +1068,40 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
10511068
}
10521069
}
10531070

1071+
/// Obtain a pointer to the stored property referred to by a key path.
1072+
///
1073+
/// If the key path represents a computed property,
1074+
/// this function will return `nil`.
1075+
///
1076+
/// - Parameter property: A `KeyPath` whose `Root` is `Pointee`.
1077+
/// - Returns: A pointer to the stored property represented
1078+
/// by the key path, or `nil`.
1079+
@inlinable
1080+
@_alwaysEmitIntoClient
1081+
public func pointer<Property>(
1082+
to property: KeyPath<Pointee, Property>
1083+
) -> UnsafePointer<Property>? {
1084+
guard let o = property._storedInlineOffset else { return nil }
1085+
return .init(Builtin.gepRaw_Word(_rawValue, o._builtinWordValue))
1086+
}
1087+
1088+
/// Obtain a mutable pointer to the stored property referred to by a key path.
1089+
///
1090+
/// If the key path represents a computed property,
1091+
/// this function will return `nil`.
1092+
///
1093+
/// - Parameter property: A `WritableKeyPath` whose `Root` is `Pointee`.
1094+
/// - Returns: A mutable pointer to the stored property represented
1095+
/// by the key path, or `nil`.
1096+
@inlinable
1097+
@_alwaysEmitIntoClient
1098+
public func pointer<Property>(
1099+
to property: WritableKeyPath<Pointee, Property>
1100+
) -> UnsafeMutablePointer<Property>? {
1101+
guard let o = property._storedInlineOffset else { return nil }
1102+
return .init(Builtin.gepRaw_Word(_rawValue, o._builtinWordValue))
1103+
}
1104+
10541105
@inlinable // unsafe-performance
10551106
internal static var _max: UnsafeMutablePointer {
10561107
return UnsafeMutablePointer(

stdlib/public/core/UnsafeRawPointer.swift

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,82 @@ extension UnsafeRawPointer: Strideable {
444444
}
445445
}
446446

447+
extension UnsafeRawPointer {
448+
/// Obtain the next pointer properly aligned to store a value of type `T`.
449+
///
450+
/// If `self` is properly aligned for accessing `T`,
451+
/// this function returns `self`.
452+
///
453+
/// - Parameters:
454+
/// - type: the type to be stored at the returned address.
455+
/// - Returns: a pointer properly aligned to store a value of type `T`.
456+
@inlinable
457+
@_alwaysEmitIntoClient
458+
public func alignedUp<T>(for type: T.Type) -> Self {
459+
let mask = UInt(Builtin.alignof(T.self)) &- 1
460+
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
461+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
462+
}
463+
464+
/// Obtain the preceding pointer properly aligned to store a value of type `T`.
465+
///
466+
/// If `self` is properly aligned for accessing `T`,
467+
/// this function returns `self`.
468+
///
469+
/// - Parameters:
470+
/// - type: the type to be stored at the returned address.
471+
/// - Returns: a pointer properly aligned to store a value of type `T`.
472+
@inlinable
473+
@_alwaysEmitIntoClient
474+
public func alignedDown<T>(for type: T.Type) -> Self {
475+
let mask = UInt(Builtin.alignof(T.self)) &- 1
476+
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
477+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
478+
}
479+
480+
/// Obtain the next pointer whose bit pattern is a multiple of `alignment`.
481+
///
482+
/// If the bit pattern of `self` is a multiple of `alignment`,
483+
/// this function returns `self`.
484+
///
485+
/// - Parameters:
486+
/// - alignment: the alignment of the returned pointer, in bytes.
487+
/// `alignment` must be a whole power of 2.
488+
/// - Returns: a pointer aligned to `alignment`.
489+
@inlinable
490+
@_alwaysEmitIntoClient
491+
public func alignedUp(toMultipleOf alignment: Int) -> Self {
492+
let mask = UInt(alignment._builtinWordValue) &- 1
493+
_debugPrecondition(
494+
alignment > 0 && UInt(alignment._builtinWordValue) & mask == 0,
495+
"alignment must be a whole power of 2."
496+
)
497+
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
498+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
499+
}
500+
501+
/// Obtain the preceding pointer whose bit pattern is a multiple of `alignment`.
502+
///
503+
/// If the bit pattern of `self` is a multiple of `alignment`,
504+
/// this function returns `self`.
505+
///
506+
/// - Parameters:
507+
/// - alignment: the alignment of the returned pointer, in bytes.
508+
/// `alignment` must be a whole power of 2.
509+
/// - Returns: a pointer aligned to `alignment`.
510+
@inlinable
511+
@_alwaysEmitIntoClient
512+
public func alignedDown(toMultipleOf alignment: Int) -> Self {
513+
let mask = UInt(alignment._builtinWordValue) &- 1
514+
_debugPrecondition(
515+
alignment > 0 && UInt(alignment._builtinWordValue) & mask == 0,
516+
"alignment must be a whole power of 2."
517+
)
518+
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
519+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
520+
}
521+
}
522+
447523
/// A raw pointer for accessing and manipulating
448524
/// untyped data.
449525
///
@@ -1145,6 +1221,82 @@ extension UnsafeMutableRawPointer: Strideable {
11451221
}
11461222
}
11471223

1224+
extension UnsafeMutableRawPointer {
1225+
/// Obtain the next pointer properly aligned to store a value of type `T`.
1226+
///
1227+
/// If `self` is properly aligned for accessing `T`,
1228+
/// this function returns `self`.
1229+
///
1230+
/// - Parameters:
1231+
/// - type: the type to be stored at the returned address.
1232+
/// - Returns: a pointer properly aligned to store a value of type `T`.
1233+
@inlinable
1234+
@_alwaysEmitIntoClient
1235+
public func alignedUp<T>(for type: T.Type) -> Self {
1236+
let mask = UInt(Builtin.alignof(T.self)) &- 1
1237+
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
1238+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
1239+
}
1240+
1241+
/// Obtain the preceding pointer properly aligned to store a value of type `T`.
1242+
///
1243+
/// If `self` is properly aligned for accessing `T`,
1244+
/// this function returns `self`.
1245+
///
1246+
/// - Parameters:
1247+
/// - type: the type to be stored at the returned address.
1248+
/// - Returns: a pointer properly aligned to store a value of type `T`.
1249+
@inlinable
1250+
@_alwaysEmitIntoClient
1251+
public func alignedDown<T>(for type: T.Type) -> Self {
1252+
let mask = UInt(Builtin.alignof(T.self)) &- 1
1253+
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
1254+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
1255+
}
1256+
1257+
/// Obtain the next pointer whose bit pattern is a multiple of `alignment`.
1258+
///
1259+
/// If the bit pattern of `self` is a multiple of `alignment`,
1260+
/// this function returns `self`.
1261+
///
1262+
/// - Parameters:
1263+
/// - alignment: the alignment of the returned pointer, in bytes.
1264+
/// `alignment` must be a whole power of 2.
1265+
/// - Returns: a pointer aligned to `alignment`.
1266+
@inlinable
1267+
@_alwaysEmitIntoClient
1268+
public func alignedUp(toMultipleOf alignment: Int) -> Self {
1269+
let mask = UInt(alignment._builtinWordValue) &- 1
1270+
_debugPrecondition(
1271+
alignment > 0 && UInt(alignment._builtinWordValue) & mask == 0,
1272+
"alignment must be a whole power of 2."
1273+
)
1274+
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
1275+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
1276+
}
1277+
1278+
/// Obtain the preceding pointer whose bit pattern is a multiple of `alignment`.
1279+
///
1280+
/// If the bit pattern of `self` is a multiple of `alignment`,
1281+
/// this function returns `self`.
1282+
///
1283+
/// - Parameters:
1284+
/// - alignment: the alignment of the returned pointer, in bytes.
1285+
/// `alignment` must be a whole power of 2.
1286+
/// - Returns: a pointer aligned to `alignment`.
1287+
@inlinable
1288+
@_alwaysEmitIntoClient
1289+
public func alignedDown(toMultipleOf alignment: Int) -> Self {
1290+
let mask = UInt(alignment._builtinWordValue) &- 1
1291+
_debugPrecondition(
1292+
alignment > 0 && UInt(alignment._builtinWordValue) & mask == 0,
1293+
"alignment must be a whole power of 2."
1294+
)
1295+
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
1296+
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
1297+
}
1298+
}
1299+
11481300
extension OpaquePointer {
11491301
@_transparent
11501302
public init(@_nonEphemeral _ from: UnsafeMutableRawPointer) {

test/stdlib/UnsafePointer.swift.gyb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,27 @@ ${SelfName}TestSuite.test("withMemoryReboundSE0333.capacity1") {
516516
}
517517
}
518518

519+
${SelfName}TestSuite.test("pointer(to:)") {
520+
struct Example {
521+
var a = false
522+
var b = 0
523+
var c: String { "\(a),\(b)" }
524+
}
525+
let allocated = UnsafeMutablePointer<Example>.allocate(capacity: 1)
526+
allocated.pointee = Example()
527+
defer { allocated.deallocate() }
528+
let p = ${SelfName}(allocated)
529+
expectEqual(p.pointer(to: \.a)!.pointee, false)
530+
expectEqual(p.pointer(to: \.b)!.pointee, 0)
531+
expectNil(p.pointer(to: \.c))
532+
% if SelfName == 'UnsafeMutablePointer':
533+
p.pointer(to: \.a)!.pointee = true
534+
p.pointer(to: \.b)!.pointee += 1
535+
expectEqual(p.pointer(to: \.a as KeyPath)!.pointee, true)
536+
expectEqual(p.pointer(to: \.b as KeyPath)!.pointee, 1)
537+
% end
538+
}
539+
519540
% end
520541

521542
runAllTests()

0 commit comments

Comments
 (0)