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
11 changes: 8 additions & 3 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,16 @@ BUILTIN_SIL_OPERATION(CastReference, "castReference", Special)
/// reinterpretCast has type T -> U.
BUILTIN_SIL_OPERATION(ReinterpretCast, "reinterpretCast", Special)

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

/// addressOfBorrow (__shared T) -> Builtin.RawPointer
/// Returns a RawPointer pointing to a borrowed rvalue. The returned pointer is only
/// valid within the scope of the borrow.
BUILTIN_SIL_OPERATION(AddressOfBorrow, "addressOfBorrow", Special)

/// GepRaw(Builtin.RawPointer, Builtin.Word) -> Builtin.RawPointer
///
/// Adds index bytes to a base pointer.
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,15 @@ ERROR(shifting_all_significant_bits,none,
ERROR(static_report_error, none,
"static report error", ())

ERROR(non_physical_addressof,none,
"addressof only works with purely physical lvalues; "
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "
"`withUnsafePointer` or `withUnsafeBytes`", ())
ERROR(non_borrowed_indirect_addressof,none,
"addressof only works with borrowable in-memory rvalues; "
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "
"`withUnsafePointer` or `withUnsafeBytes`", ())

REMARK(opt_remark_passed, none, "%0", (StringRef))
REMARK(opt_remark_missed, none, "%0", (StringRef))

Expand Down
15 changes: 14 additions & 1 deletion lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,15 @@ static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) {
return builder.build(Id);
}

static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context,
Identifier Id) {
// <T> (T) -> RawPointer
BuiltinGenericSignatureBuilder builder(Context);
builder.addParameter(makeGenericParam());
builder.setResult(makeConcrete(Context.TheRawPointerType));
return builder.build(Id);
}

static ValueDecl *getTypeJoinOperation(ASTContext &Context, Identifier Id) {
// <T,U,V> (T.Type, U.Type) -> V.Type
BuiltinGenericSignatureBuilder builder(Context, 3);
Expand Down Expand Up @@ -1814,7 +1823,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
case BuiltinValueKind::AddressOf:
if (!Types.empty()) return nullptr;
return getAddressOfOperation(Context, Id);


case BuiltinValueKind::AddressOfBorrow:
if (!Types.empty()) return nullptr;
return getAddressOfBorrowOperation(Context, Id);

case BuiltinValueKind::CondFail:
return getCondFailOperation(Context, Id);

Expand Down
1 change: 1 addition & 0 deletions lib/SIL/SILOwnershipVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,7 @@ BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BridgeFromRawPointer)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastReference)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ReinterpretCast)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOf)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOfBorrow)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GepRaw)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Gep)
BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GetTailAddr)
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/ValueOwnershipKindClassifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, BridgeToRawPointer)
CONSTANT_OWNERSHIP_BUILTIN(Unowned, BridgeFromRawPointer)
CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastReference)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOf)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOfBorrow)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, GepRaw)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, Gep)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, GetTailAddr)
Expand Down
45 changes: 41 additions & 4 deletions lib/SILGen/SILGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,14 +447,51 @@ static ManagedValue emitBuiltinBridgeFromRawPointer(SILGenFunction &SGF,
static ManagedValue emitBuiltinAddressOf(SILGenFunction &SGF,
SILLocation loc,
SubstitutionList substitutions,
ArrayRef<ManagedValue> args,
Expr *argument,
SGFContext C) {
assert(args.size() == 1 && "addressof should have a single argument");
auto rawPointerTy = SILType::getRawPointerType(SGF.getASTContext());
// If the argument is inout, try forming its lvalue. This builtin only works
// if it's trivially physically projectable.
auto inout = cast<InOutExpr>(argument->getSemanticsProvidingExpr());
auto lv = SGF.emitLValue(inout->getSubExpr(), AccessKind::ReadWrite);
if (!lv.isPhysical() || !lv.isLoadingPure()) {
SGF.SGM.diagnose(argument->getLoc(), diag::non_physical_addressof);
return ManagedValue::forUnmanaged(SILUndef::get(rawPointerTy, &SGF.SGM.M));
}

auto addr = SGF.emitAddressOfLValue(argument, std::move(lv),
AccessKind::ReadWrite)
.getValue();

// Take the address argument and cast it to RawPointer.
SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext());
SILValue result = SGF.B.createAddressToPointer(loc,
args[0].getUnmanagedValue(),
SILValue result = SGF.B.createAddressToPointer(loc, addr,
rawPointerType);
return ManagedValue::forUnmanaged(result);
}

/// Specialized emitter for Builtin.addressOfBorrow.
static ManagedValue emitBuiltinAddressOfBorrow(SILGenFunction &SGF,
SILLocation loc,
SubstitutionList substitutions,
Expr *argument,
SGFContext C) {
auto rawPointerTy = SILType::getRawPointerType(SGF.getASTContext());
SILValue addr;
// Try to borrow the argument at +0. We only support if it's
// naturally emitted borrowed in memory.
auto borrow = SGF.emitRValue(argument, SGFContext::AllowGuaranteedPlusZero)
.getAsSingleValue(SGF, argument);
if (!borrow.isPlusZero() || !borrow.getType().isAddress()) {
SGF.SGM.diagnose(argument->getLoc(), diag::non_borrowed_indirect_addressof);
return ManagedValue::forUnmanaged(SILUndef::get(rawPointerTy, &SGF.SGM.M));
}

addr = borrow.getValue();

// Take the address argument and cast it to RawPointer.
SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext());
SILValue result = SGF.B.createAddressToPointer(loc, addr,
rawPointerType);
return ManagedValue::forUnmanaged(result);
}
Expand Down
59 changes: 51 additions & 8 deletions stdlib/public/core/LifetimeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,24 @@ public func _fixLifetime<T>(_ x: T) {
/// later use.
///
/// - Parameters:
/// - arg: An instance to temporarily use via pointer.
/// - body: A closure that takes a mutable pointer to `arg` as its sole
/// - value: An instance to temporarily use via pointer. Note that the `inout`
/// exclusivity rules mean that, like any other `inout` argument, `value`
/// cannot be directly accessed by other code for the duration of `body`.
/// Access must only occur through the pointer argument to `body` until
/// `body` returns.
/// - body: A closure that takes a mutable pointer to `value` as its sole
/// argument. If the closure has a return value, that value is also used
/// as the return value of the `withUnsafeMutablePointer(to:_:)` function.
/// The pointer argument is valid only for the duration of the function's
/// execution.
/// - Returns: The return value, if any, of the `body` closure.
@inlinable
public func withUnsafeMutablePointer<T, Result>(
to arg: inout T,
to value: inout T,
_ body: (UnsafeMutablePointer<T>) throws -> Result
) rethrows -> Result
{
return try body(UnsafeMutablePointer<T>(Builtin.addressof(&arg)))
return try body(UnsafeMutablePointer<T>(Builtin.addressof(&value)))
}

/// Invokes the given closure with a pointer to the given argument.
Expand All @@ -115,17 +119,56 @@ public func withUnsafeMutablePointer<T, Result>(
/// use.
///
/// - Parameters:
/// - arg: An instance to temporarily use via pointer.
/// - body: A closure that takes a pointer to `arg` as its sole argument. If
/// - value: An instance to temporarily use via pointer.
/// - body: A closure that takes a pointer to `value` as its sole argument. If
/// the closure has a return value, that value is also used as the return
/// value of the `withUnsafePointer(to:_:)` function. The pointer argument
/// is valid only for the duration of the function's execution.
/// It is undefined behavior to try to mutate through the pointer argument
/// by converting it to `UnsafeMutablePointer` or any other mutable pointer
/// type. If you need to mutate the argument through the pointer, use
/// `withUnsafeMutablePointer(to:_:)` instead.
/// - Returns: The return value, if any, of the `body` closure.
@inlinable
public func withUnsafePointer<T, Result>(
to arg: inout T,
to value: T,
_ body: (UnsafePointer<T>) throws -> Result
) rethrows -> Result
{
return try body(UnsafePointer<T>(Builtin.addressof(&arg)))
return try body(UnsafePointer<T>(Builtin.addressOfBorrow(value)))
}

/// Invokes the given closure with a pointer to the given argument.
///
/// The `withUnsafePointer(to:_:)` function is useful for calling Objective-C
/// APIs that take in parameters by const pointer.
///
/// The pointer argument to `body` is valid only during the execution of
/// `withUnsafePointer(to:_:)`. Do not store or return the pointer for later
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the parameters to the function name where this is mentioned? withUnsafeMutablePointer(to:_:)

/// use.
///
/// - Parameters:
/// - value: An instance to temporarily use via pointer. Note that the `inout`
/// exclusivity rules mean that, like any other `inout` argument, `value`
/// cannot be directly accessed by other code for the duration of `body`.
/// Access must only occur through the pointer argument to `body` until
/// `body` returns.
/// - body: A closure that takes a pointer to `value` as its sole argument. If
/// the closure has a return value, that value is also used as the return
/// value of the `withUnsafePointer(to:_:)` function. The pointer argument
/// is valid only for the duration of the function's execution.
/// It is undefined behavior to try to mutate through the pointer argument
/// by converting it to `UnsafeMutablePointer` or any other mutable pointer
/// type. If you need to mutate the argument through the pointer, use
/// `withUnsafeMutablePointer(to:_:)` instead.
/// - Returns: The return value, if any, of the `body` closure.
@_inlineable
public func withUnsafePointer<T, Result>(
to value: inout T,
_ body: (UnsafePointer<T>) throws -> Result
) rethrows -> Result
{
return try body(UnsafePointer<T>(Builtin.addressof(&value)))
}


67 changes: 54 additions & 13 deletions stdlib/public/core/UnsafeRawBufferPointer.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -724,25 +724,29 @@ extension ${Self} {
/// bytes of the given argument.
///
/// The buffer pointer argument to the `body` closure provides a collection
/// interface to the raw bytes of `arg`. The buffer is the size of the
/// instance passed as `arg` and does not include any remote storage.
/// interface to the raw bytes of `value`. The buffer is the size of the
/// instance passed as `value` and does not include any remote storage.
///
/// - Parameters:
/// - arg: An instance to temporarily access through a mutable raw buffer
/// - value: An instance to temporarily access through a mutable raw buffer
/// pointer.
/// - body: A closure that takes a raw buffer pointer to the bytes of `arg`
/// Note that the `inout` exclusivity rules mean that, like any other
/// `inout` argument, `value` cannot be directly accessed by other code
/// for the duration of `body`. Access must only occur through the pointer
/// argument to `body` until `body` returns.
/// - body: A closure that takes a raw buffer pointer to the bytes of `value`
/// as its sole argument. If the closure has a return value, that value is
/// also used as the return value of the `withUnsafeMutableBytes(of:_:)`
/// function. The buffer pointer argument is valid only for the duration
/// of the closure's execution.
/// - Returns: The return value, if any, of the `body` closure.
@inlinable
public func withUnsafeMutableBytes<T, Result>(
of arg: inout T,
of value: inout T,
_ body: (UnsafeMutableRawBufferPointer) throws -> Result
) rethrows -> Result
{
return try withUnsafeMutablePointer(to: &arg) {
return try withUnsafeMutablePointer(to: &value) {
return try body(UnsafeMutableRawBufferPointer(
start: $0, count: MemoryLayout<T>.size))
}
Expand All @@ -752,28 +756,65 @@ public func withUnsafeMutableBytes<T, Result>(
/// the given argument.
///
/// The buffer pointer argument to the `body` closure provides a collection
/// interface to the raw bytes of `arg`. The buffer is the size of the
/// instance passed as `arg` and does not include any remote storage.
/// interface to the raw bytes of `value`. The buffer is the size of the
/// instance passed as `value` and does not include any remote storage.
///
/// - Parameters:
/// - arg: An instance to temporarily access through a raw buffer pointer.
/// - body: A closure that takes a raw buffer pointer to the bytes of `arg`
/// - value: An instance to temporarily access through a raw buffer pointer.
/// Note that the `inout` exclusivity rules mean that, like any other
/// `inout` argument, `value` cannot be directly accessed by other code
/// for the duration of `body`. Access must only occur through the pointer
/// argument to `body` until `body` returns.
/// - body: A closure that takes a raw buffer pointer to the bytes of `value`
/// as its sole argument. If the closure has a return value, that value is
/// also used as the return value of the `withUnsafeBytes(of:_:)`
/// function. The buffer pointer argument is valid only for the duration
/// of the closure's execution.
/// of the closure's execution. It is undefined behavior to attempt to
/// mutate through the pointer by conversion to
/// `UnsafeMutableRawBufferPointer` or any other mutable pointer type.
/// If you want to mutate a value by writing through a pointer, use
/// `withUnsafeMutableBytes(of:_:)` instead.
/// - Returns: The return value, if any, of the `body` closure.
@inlinable
public func withUnsafeBytes<T, Result>(
of arg: inout T,
of value: inout T,
_ body: (UnsafeRawBufferPointer) throws -> Result
) rethrows -> Result
{
return try withUnsafePointer(to: &arg) {
return try withUnsafePointer(to: &value) {
try body(UnsafeRawBufferPointer(start: $0, count: MemoryLayout<T>.size))
}
}

/// Invokes the given closure with a buffer pointer covering the raw bytes of
/// the given argument.
///
/// The buffer pointer argument to the `body` closure provides a collection
/// interface to the raw bytes of `value`. The buffer is the size of the
/// instance passed as `value` and does not include any remote storage.
///
/// - Parameters:
/// - value: An instance to temporarily access through a raw buffer pointer.
/// - body: A closure that takes a raw buffer pointer to the bytes of `value`
/// as its sole argument. If the closure has a return value, that value is
/// also used as the return value of the `withUnsafeBytes(of:_:)`
/// function. The buffer pointer argument is valid only for the duration
/// of the closure's execution. It is undefined behavior to attempt to
/// mutate through the pointer by conversion to
/// `UnsafeMutableRawBufferPointer` or any other mutable pointer type.
/// If you want to mutate a value by writing through a pointer, use
/// `withUnsafeMutableBytes(of:_:)` instead.
/// - Returns: The return value, if any, of the `body` closure.
@_inlineable
public func withUnsafeBytes<T, Result>(
of value: T,
_ body: (UnsafeRawBufferPointer) throws -> Result
) rethrows -> Result {
let addr = UnsafeRawPointer(Builtin.addressOfBorrow(value))
let buffer = UnsafeRawBufferPointer(start: addr, count: MemoryLayout<T>.size)
return try body(buffer)
}

// @available(*, deprecated, renamed: "UnsafeRawBufferPointer.Iterator")
public typealias UnsafeRawBufferPointerIterator<T> = UnsafeBufferPointer<T>.Iterator

Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ func rdar27391581(_ a : Int, b : Int) -> Int {
func read2(_ p: UnsafeMutableRawPointer, maxLength: Int) {}
func read<T : BinaryInteger>() -> T? {
var buffer : T
let n = withUnsafePointer(to: &buffer) { (p) in
let n = withUnsafeMutablePointer(to: &buffer) { (p) in
read2(UnsafePointer(p), maxLength: MemoryLayout<T>.size) // expected-error {{cannot convert value of type 'UnsafePointer<_>' to expected argument type 'UnsafeMutableRawPointer'}}
}
}
Expand Down