Skip to content

Commit 62771a0

Browse files
committed
stdlib: Add withUnsafeBytes(of:) and withUnsafePointer(to:) for immutable arguments.
Since the functions produce pointers with tightly-scoped lifetimes there's no formal reason these have to only work on `inout` things. Now that arguments can be +0, we can even do this without copying values that we already have at +0.
1 parent f1076fd commit 62771a0

File tree

9 files changed

+180
-30
lines changed

9 files changed

+180
-30
lines changed

include/swift/AST/Builtins.def

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,16 @@ BUILTIN_SIL_OPERATION(CastReference, "castReference", Special)
272272
/// reinterpretCast has type T -> U.
273273
BUILTIN_SIL_OPERATION(ReinterpretCast, "reinterpretCast", Special)
274274

275-
/// addressof ([inout] T) -> Builtin.RawPointer
276-
/// Returns a RawPointer pointing to an lvalue. The returned pointer is only
277-
/// valid within the scope of the statement for logical lvalues.
275+
/// addressof (inout T) -> Builtin.RawPointer
276+
/// Returns a RawPointer pointing to a physical lvalue. The returned pointer is
277+
/// only valid for the duration of the original binding.
278278
BUILTIN_SIL_OPERATION(AddressOf, "addressof", Special)
279279

280+
/// addressOfBorrow (__shared T) -> Builtin.RawPointer
281+
/// Returns a RawPointer pointing to a borrowed rvalue. The returned pointer is only
282+
/// valid within the scope of the borrow.
283+
BUILTIN_SIL_OPERATION(AddressOfBorrow, "addressOfBorrow", Special)
284+
280285
/// GepRaw(Builtin.RawPointer, Builtin.Word) -> Builtin.RawPointer
281286
///
282287
/// Adds index bytes to a base pointer.

include/swift/AST/DiagnosticsSIL.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,15 @@ ERROR(shifting_all_significant_bits,none,
300300
ERROR(static_report_error, none,
301301
"static report error", ())
302302

303+
ERROR(non_physical_addressof,none,
304+
"addressof only works with purely physical lvalues; "
305+
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "
306+
"`withUnsafePointer` or `withUnsafeBytes`", ())
307+
ERROR(non_borrowed_indirect_addressof,none,
308+
"addressof only works with borrowable in-memory rvalues; "
309+
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "
310+
"`withUnsafePointer` or `withUnsafeBytes`", ())
311+
303312
REMARK(opt_remark_passed, none, "%0", (StringRef))
304313
REMARK(opt_remark_missed, none, "%0", (StringRef))
305314

lib/AST/Builtins.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,15 @@ static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) {
991991
return builder.build(Id);
992992
}
993993

994+
static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context,
995+
Identifier Id) {
996+
// <T> (T) -> RawPointer
997+
BuiltinGenericSignatureBuilder builder(Context);
998+
builder.addParameter(makeGenericParam());
999+
builder.setResult(makeConcrete(Context.TheRawPointerType));
1000+
return builder.build(Id);
1001+
}
1002+
9941003
static ValueDecl *getTypeJoinOperation(ASTContext &Context, Identifier Id) {
9951004
// <T,U,V> (T.Type, U.Type) -> V.Type
9961005
BuiltinGenericSignatureBuilder builder(Context, 3);
@@ -1814,7 +1823,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
18141823
case BuiltinValueKind::AddressOf:
18151824
if (!Types.empty()) return nullptr;
18161825
return getAddressOfOperation(Context, Id);
1817-
1826+
1827+
case BuiltinValueKind::AddressOfBorrow:
1828+
if (!Types.empty()) return nullptr;
1829+
return getAddressOfBorrowOperation(Context, Id);
1830+
18181831
case BuiltinValueKind::CondFail:
18191832
return getCondFailOperation(Context, Id);
18201833

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,7 @@ BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BridgeFromRawPointer)
14281428
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastReference)
14291429
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ReinterpretCast)
14301430
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOf)
1431+
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOfBorrow)
14311432
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GepRaw)
14321433
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Gep)
14331434
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GetTailAddr)

lib/SIL/ValueOwnershipKindClassifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, BridgeToRawPointer)
472472
CONSTANT_OWNERSHIP_BUILTIN(Unowned, BridgeFromRawPointer)
473473
CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastReference)
474474
CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOf)
475+
CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOfBorrow)
475476
CONSTANT_OWNERSHIP_BUILTIN(Trivial, GepRaw)
476477
CONSTANT_OWNERSHIP_BUILTIN(Trivial, Gep)
477478
CONSTANT_OWNERSHIP_BUILTIN(Trivial, GetTailAddr)

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -447,14 +447,51 @@ static ManagedValue emitBuiltinBridgeFromRawPointer(SILGenFunction &SGF,
447447
static ManagedValue emitBuiltinAddressOf(SILGenFunction &SGF,
448448
SILLocation loc,
449449
SubstitutionList substitutions,
450-
ArrayRef<ManagedValue> args,
450+
Expr *argument,
451451
SGFContext C) {
452-
assert(args.size() == 1 && "addressof should have a single argument");
452+
auto rawPointerTy = SILType::getRawPointerType(SGF.getASTContext());
453+
// If the argument is inout, try forming its lvalue. This builtin only works
454+
// if it's trivially physically projectable.
455+
auto inout = cast<InOutExpr>(argument->getSemanticsProvidingExpr());
456+
auto lv = SGF.emitLValue(inout->getSubExpr(), AccessKind::ReadWrite);
457+
if (!lv.isPhysical() || !lv.isLoadingPure()) {
458+
SGF.SGM.diagnose(argument->getLoc(), diag::non_physical_addressof);
459+
return ManagedValue::forUnmanaged(SILUndef::get(rawPointerTy, &SGF.SGM.M));
460+
}
461+
462+
auto addr = SGF.emitAddressOfLValue(argument, std::move(lv),
463+
AccessKind::ReadWrite)
464+
.getValue();
453465

454466
// Take the address argument and cast it to RawPointer.
455467
SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext());
456-
SILValue result = SGF.B.createAddressToPointer(loc,
457-
args[0].getUnmanagedValue(),
468+
SILValue result = SGF.B.createAddressToPointer(loc, addr,
469+
rawPointerType);
470+
return ManagedValue::forUnmanaged(result);
471+
}
472+
473+
/// Specialized emitter for Builtin.addressOfBorrow.
474+
static ManagedValue emitBuiltinAddressOfBorrow(SILGenFunction &SGF,
475+
SILLocation loc,
476+
SubstitutionList substitutions,
477+
Expr *argument,
478+
SGFContext C) {
479+
auto rawPointerTy = SILType::getRawPointerType(SGF.getASTContext());
480+
SILValue addr;
481+
// Try to borrow the argument at +0. We only support if it's
482+
// naturally emitted borrowed in memory.
483+
auto borrow = SGF.emitRValue(argument, SGFContext::AllowGuaranteedPlusZero)
484+
.getAsSingleValue(SGF, argument);
485+
if (!borrow.isPlusZero() || !borrow.getType().isAddress()) {
486+
SGF.SGM.diagnose(argument->getLoc(), diag::non_borrowed_indirect_addressof);
487+
return ManagedValue::forUnmanaged(SILUndef::get(rawPointerTy, &SGF.SGM.M));
488+
}
489+
490+
addr = borrow.getValue();
491+
492+
// Take the address argument and cast it to RawPointer.
493+
SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext());
494+
SILValue result = SGF.B.createAddressToPointer(loc, addr,
458495
rawPointerType);
459496
return ManagedValue::forUnmanaged(result);
460497
}

stdlib/public/core/LifetimeManager.swift

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,24 @@ public func _fixLifetime<T>(_ x: T) {
8888
/// later use.
8989
///
9090
/// - Parameters:
91-
/// - arg: An instance to temporarily use via pointer.
92-
/// - body: A closure that takes a mutable pointer to `arg` as its sole
91+
/// - value: An instance to temporarily use via pointer. Note that the `inout`
92+
/// exclusivity rules mean that, like any other `inout` argument, `value`
93+
/// cannot be directly accessed by other code for the duration of `body`.
94+
/// Access must only occur through the pointer argument to `body` until
95+
/// `body` returns.
96+
/// - body: A closure that takes a mutable pointer to `value` as its sole
9397
/// argument. If the closure has a return value, that value is also used
9498
/// as the return value of the `withUnsafeMutablePointer(to:_:)` function.
9599
/// The pointer argument is valid only for the duration of the function's
96100
/// execution.
97101
/// - Returns: The return value, if any, of the `body` closure.
98102
@inlinable
99103
public func withUnsafeMutablePointer<T, Result>(
100-
to arg: inout T,
104+
to value: inout T,
101105
_ body: (UnsafeMutablePointer<T>) throws -> Result
102106
) rethrows -> Result
103107
{
104-
return try body(UnsafeMutablePointer<T>(Builtin.addressof(&arg)))
108+
return try body(UnsafeMutablePointer<T>(Builtin.addressof(&value)))
105109
}
106110

107111
/// Invokes the given closure with a pointer to the given argument.
@@ -115,17 +119,56 @@ public func withUnsafeMutablePointer<T, Result>(
115119
/// use.
116120
///
117121
/// - Parameters:
118-
/// - arg: An instance to temporarily use via pointer.
119-
/// - body: A closure that takes a pointer to `arg` as its sole argument. If
122+
/// - value: An instance to temporarily use via pointer.
123+
/// - body: A closure that takes a pointer to `value` as its sole argument. If
120124
/// the closure has a return value, that value is also used as the return
121125
/// value of the `withUnsafePointer(to:_:)` function. The pointer argument
122126
/// is valid only for the duration of the function's execution.
127+
/// It is undefined behavior to try to mutate through the pointer argument
128+
/// by converting it to `UnsafeMutablePointer` or any other mutable pointer
129+
/// type. If you need to mutate the argument through the pointer, use
130+
/// `withUnsafeMutablePointer(to:_:)` instead.
123131
/// - Returns: The return value, if any, of the `body` closure.
124132
@inlinable
125133
public func withUnsafePointer<T, Result>(
126-
to arg: inout T,
134+
to value: T,
127135
_ body: (UnsafePointer<T>) throws -> Result
128136
) rethrows -> Result
129137
{
130-
return try body(UnsafePointer<T>(Builtin.addressof(&arg)))
138+
return try body(UnsafePointer<T>(Builtin.addressOfBorrow(value)))
131139
}
140+
141+
/// Invokes the given closure with a pointer to the given argument.
142+
///
143+
/// The `withUnsafePointer(to:_:)` function is useful for calling Objective-C
144+
/// APIs that take in parameters by const pointer.
145+
///
146+
/// The pointer argument to `body` is valid only during the execution of
147+
/// `withUnsafePointer(to:_:)`. Do not store or return the pointer for later
148+
/// use.
149+
///
150+
/// - Parameters:
151+
/// - value: An instance to temporarily use via pointer. Note that the `inout`
152+
/// exclusivity rules mean that, like any other `inout` argument, `value`
153+
/// cannot be directly accessed by other code for the duration of `body`.
154+
/// Access must only occur through the pointer argument to `body` until
155+
/// `body` returns.
156+
/// - body: A closure that takes a pointer to `value` as its sole argument. If
157+
/// the closure has a return value, that value is also used as the return
158+
/// value of the `withUnsafePointer(to:_:)` function. The pointer argument
159+
/// is valid only for the duration of the function's execution.
160+
/// It is undefined behavior to try to mutate through the pointer argument
161+
/// by converting it to `UnsafeMutablePointer` or any other mutable pointer
162+
/// type. If you need to mutate the argument through the pointer, use
163+
/// `withUnsafeMutablePointer(to:_:)` instead.
164+
/// - Returns: The return value, if any, of the `body` closure.
165+
@_inlineable
166+
public func withUnsafePointer<T, Result>(
167+
to value: inout T,
168+
_ body: (UnsafePointer<T>) throws -> Result
169+
) rethrows -> Result
170+
{
171+
return try body(UnsafePointer<T>(Builtin.addressof(&value)))
172+
}
173+
174+

stdlib/public/core/UnsafeRawBufferPointer.swift.gyb

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -724,25 +724,29 @@ extension ${Self} {
724724
/// bytes of the given argument.
725725
///
726726
/// The buffer pointer argument to the `body` closure provides a collection
727-
/// interface to the raw bytes of `arg`. The buffer is the size of the
728-
/// instance passed as `arg` and does not include any remote storage.
727+
/// interface to the raw bytes of `value`. The buffer is the size of the
728+
/// instance passed as `value` and does not include any remote storage.
729729
///
730730
/// - Parameters:
731-
/// - arg: An instance to temporarily access through a mutable raw buffer
731+
/// - value: An instance to temporarily access through a mutable raw buffer
732732
/// pointer.
733-
/// - body: A closure that takes a raw buffer pointer to the bytes of `arg`
733+
/// Note that the `inout` exclusivity rules mean that, like any other
734+
/// `inout` argument, `value` cannot be directly accessed by other code
735+
/// for the duration of `body`. Access must only occur through the pointer
736+
/// argument to `body` until `body` returns.
737+
/// - body: A closure that takes a raw buffer pointer to the bytes of `value`
734738
/// as its sole argument. If the closure has a return value, that value is
735739
/// also used as the return value of the `withUnsafeMutableBytes(of:_:)`
736740
/// function. The buffer pointer argument is valid only for the duration
737741
/// of the closure's execution.
738742
/// - Returns: The return value, if any, of the `body` closure.
739743
@inlinable
740744
public func withUnsafeMutableBytes<T, Result>(
741-
of arg: inout T,
745+
of value: inout T,
742746
_ body: (UnsafeMutableRawBufferPointer) throws -> Result
743747
) rethrows -> Result
744748
{
745-
return try withUnsafeMutablePointer(to: &arg) {
749+
return try withUnsafeMutablePointer(to: &value) {
746750
return try body(UnsafeMutableRawBufferPointer(
747751
start: $0, count: MemoryLayout<T>.size))
748752
}
@@ -752,28 +756,65 @@ public func withUnsafeMutableBytes<T, Result>(
752756
/// the given argument.
753757
///
754758
/// The buffer pointer argument to the `body` closure provides a collection
755-
/// interface to the raw bytes of `arg`. The buffer is the size of the
756-
/// instance passed as `arg` and does not include any remote storage.
759+
/// interface to the raw bytes of `value`. The buffer is the size of the
760+
/// instance passed as `value` and does not include any remote storage.
757761
///
758762
/// - Parameters:
759-
/// - arg: An instance to temporarily access through a raw buffer pointer.
760-
/// - body: A closure that takes a raw buffer pointer to the bytes of `arg`
763+
/// - value: An instance to temporarily access through a raw buffer pointer.
764+
/// Note that the `inout` exclusivity rules mean that, like any other
765+
/// `inout` argument, `value` cannot be directly accessed by other code
766+
/// for the duration of `body`. Access must only occur through the pointer
767+
/// argument to `body` until `body` returns.
768+
/// - body: A closure that takes a raw buffer pointer to the bytes of `value`
761769
/// as its sole argument. If the closure has a return value, that value is
762770
/// also used as the return value of the `withUnsafeBytes(of:_:)`
763771
/// function. The buffer pointer argument is valid only for the duration
764-
/// of the closure's execution.
772+
/// of the closure's execution. It is undefined behavior to attempt to
773+
/// mutate through the pointer by conversion to
774+
/// `UnsafeMutableRawBufferPointer` or any other mutable pointer type.
775+
/// If you want to mutate a value by writing through a pointer, use
776+
/// `withUnsafeMutableBytes(of:_:)` instead.
765777
/// - Returns: The return value, if any, of the `body` closure.
766778
@inlinable
767779
public func withUnsafeBytes<T, Result>(
768-
of arg: inout T,
780+
of value: inout T,
769781
_ body: (UnsafeRawBufferPointer) throws -> Result
770782
) rethrows -> Result
771783
{
772-
return try withUnsafePointer(to: &arg) {
784+
return try withUnsafePointer(to: &value) {
773785
try body(UnsafeRawBufferPointer(start: $0, count: MemoryLayout<T>.size))
774786
}
775787
}
776788

789+
/// Invokes the given closure with a buffer pointer covering the raw bytes of
790+
/// the given argument.
791+
///
792+
/// The buffer pointer argument to the `body` closure provides a collection
793+
/// interface to the raw bytes of `value`. The buffer is the size of the
794+
/// instance passed as `value` and does not include any remote storage.
795+
///
796+
/// - Parameters:
797+
/// - value: An instance to temporarily access through a raw buffer pointer.
798+
/// - body: A closure that takes a raw buffer pointer to the bytes of `value`
799+
/// as its sole argument. If the closure has a return value, that value is
800+
/// also used as the return value of the `withUnsafeBytes(of:_:)`
801+
/// function. The buffer pointer argument is valid only for the duration
802+
/// of the closure's execution. It is undefined behavior to attempt to
803+
/// mutate through the pointer by conversion to
804+
/// `UnsafeMutableRawBufferPointer` or any other mutable pointer type.
805+
/// If you want to mutate a value by writing through a pointer, use
806+
/// `withUnsafeMutableBytes(of:_:)` instead.
807+
/// - Returns: The return value, if any, of the `body` closure.
808+
@_inlineable
809+
public func withUnsafeBytes<T, Result>(
810+
of value: T,
811+
_ body: (UnsafeRawBufferPointer) throws -> Result
812+
) rethrows -> Result {
813+
let addr = UnsafeRawPointer(Builtin.addressOfBorrow(value))
814+
let buffer = UnsafeRawBufferPointer(start: addr, count: MemoryLayout<T>.size)
815+
return try body(buffer)
816+
}
817+
777818
// @available(*, deprecated, renamed: "UnsafeRawBufferPointer.Iterator")
778819
public typealias UnsafeRawBufferPointerIterator<T> = UnsafeBufferPointer<T>.Iterator
779820

test/Constraints/diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ func rdar27391581(_ a : Int, b : Int) -> Int {
750750
func read2(_ p: UnsafeMutableRawPointer, maxLength: Int) {}
751751
func read<T : BinaryInteger>() -> T? {
752752
var buffer : T
753-
let n = withUnsafePointer(to: &buffer) { (p) in
753+
let n = withUnsafeMutablePointer(to: &buffer) { (p) in
754754
read2(UnsafePointer(p), maxLength: MemoryLayout<T>.size) // expected-error {{cannot convert value of type 'UnsafePointer<_>' to expected argument type 'UnsafeMutableRawPointer'}}
755755
}
756756
}

0 commit comments

Comments
 (0)