diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 170e537d1eed0..ab17d2a0109f8 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -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. diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index be0eaded360ce..c6f37a619ab00 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -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)) diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 0e5b40b55e63e..797e39f72bd48 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -991,6 +991,15 @@ static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) { return builder.build(Id); } +static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context, + Identifier Id) { + // (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.Type, U.Type) -> V.Type BuiltinGenericSignatureBuilder builder(Context, 3); @@ -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); diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index c0fd9118a0b74..400e59b76c981 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -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) diff --git a/lib/SIL/ValueOwnershipKindClassifier.cpp b/lib/SIL/ValueOwnershipKindClassifier.cpp index 381300987f7a1..dcface093c577 100644 --- a/lib/SIL/ValueOwnershipKindClassifier.cpp +++ b/lib/SIL/ValueOwnershipKindClassifier.cpp @@ -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) diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 59cc412d317d8..c9dfcd2a9a89a 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -447,14 +447,51 @@ static ManagedValue emitBuiltinBridgeFromRawPointer(SILGenFunction &SGF, static ManagedValue emitBuiltinAddressOf(SILGenFunction &SGF, SILLocation loc, SubstitutionList substitutions, - ArrayRef 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(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); } diff --git a/stdlib/public/core/LifetimeManager.swift b/stdlib/public/core/LifetimeManager.swift index 86c138841c643..8797b316aa94e 100644 --- a/stdlib/public/core/LifetimeManager.swift +++ b/stdlib/public/core/LifetimeManager.swift @@ -88,8 +88,12 @@ public func _fixLifetime(_ 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 @@ -97,11 +101,11 @@ public func _fixLifetime(_ x: T) { /// - Returns: The return value, if any, of the `body` closure. @inlinable public func withUnsafeMutablePointer( - to arg: inout T, + to value: inout T, _ body: (UnsafeMutablePointer) throws -> Result ) rethrows -> Result { - return try body(UnsafeMutablePointer(Builtin.addressof(&arg))) + return try body(UnsafeMutablePointer(Builtin.addressof(&value))) } /// Invokes the given closure with a pointer to the given argument. @@ -115,17 +119,56 @@ public func withUnsafeMutablePointer( /// 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( - to arg: inout T, + to value: T, _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { - return try body(UnsafePointer(Builtin.addressof(&arg))) + return try body(UnsafePointer(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 +/// 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( + to value: inout T, + _ body: (UnsafePointer) throws -> Result +) rethrows -> Result +{ + return try body(UnsafePointer(Builtin.addressof(&value))) +} + + diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index 9b9443aee822c..f602c4c327b12 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -724,13 +724,17 @@ 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 @@ -738,11 +742,11 @@ extension ${Self} { /// - Returns: The return value, if any, of the `body` closure. @inlinable public func withUnsafeMutableBytes( - 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.size)) } @@ -752,28 +756,65 @@ public func withUnsafeMutableBytes( /// 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( - 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.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( + of value: T, + _ body: (UnsafeRawBufferPointer) throws -> Result +) rethrows -> Result { + let addr = UnsafeRawPointer(Builtin.addressOfBorrow(value)) + let buffer = UnsafeRawBufferPointer(start: addr, count: MemoryLayout.size) + return try body(buffer) +} + // @available(*, deprecated, renamed: "UnsafeRawBufferPointer.Iterator") public typealias UnsafeRawBufferPointerIterator = UnsafeBufferPointer.Iterator diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 86ce2e7c64843..6701990daf576 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -750,7 +750,7 @@ func rdar27391581(_ a : Int, b : Int) -> Int { func read2(_ p: UnsafeMutableRawPointer, maxLength: Int) {} func read() -> T? { var buffer : T - let n = withUnsafePointer(to: &buffer) { (p) in + let n = withUnsafeMutablePointer(to: &buffer) { (p) in read2(UnsafePointer(p), maxLength: MemoryLayout.size) // expected-error {{cannot convert value of type 'UnsafePointer<_>' to expected argument type 'UnsafeMutableRawPointer'}} } }