Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-1811] Swift 3 regression related to inout closure params and protocols #44420

Closed
jepers opened this issue Jun 18, 2016 · 5 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself regression swift 3.0 type checker Area → compiler: Semantic analysis

Comments

@jepers
Copy link

jepers commented Jun 18, 2016

Previous ID SR-1811
Radar rdar://problem/26902750
Original Reporter @jepers
Type Bug
Status Closed
Resolution Duplicate
Environment

OS X 10.11.5 (15F34), Xcode 8.0 beta (8S128d), Xcode 7.3.1

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, 3.0Regression, TypeChecker
Assignee None
Priority Medium

md5: 102c68ad72df56e90c563afcc1ec867c

duplicates:

  • SR-3479 Segmentation fault and other error for closure with inout parameter

Issue Description:

Below is the same short program in two versions, one for Swift 3 (Xcode 8 beta) and the other for Swift 2.2 (Xcode 7.3.1).

The version for Swift 3 demonstrates a compiler bug that I'm pretty sure was resolved a "long" time ago. I can't find the bug report right now but I might try to find it again later.

Bug/regression-demonstrating Swift 3 version (Xcode 8.0 beta (8S128d)):

struct PxBGRAsRGB {
    var b, g, r, a : UInt8
}
protocol RasterProtocol: class {
    associatedtype Pixel
    var width: Int { get }
    var height: Int { get }
    var rowBytes: Int { get }
    var data: UnsafeMutablePointer<UInt8> { get }
    subscript(x: Int, y: Int) -> Pixel { get set }
}
extension RasterProtocol {
    subscript(x: Int, y: Int) -> Pixel {
        get {
            precondition(x >= 0 && y >= 0 && x < width && y < height)
            return UnsafePointer<Pixel>(data.advanced(by: x * strideof(Pixel) + y * rowBytes)).pointee
        }
        set {
            precondition(x >= 0 && y >= 0 && x < width && y < height)
            UnsafeMutablePointer<Pixel>(data.advanced(by: x * strideof(Pixel) + y * rowBytes)).pointee = newValue
        }
    }
    func withEachPixel(inPlacePixelOp: @noescape (p: inout Pixel) -> Void) {
        for y in 0 ..< height { for x in 0 ..< width { inPlacePixelOp(p: &self[x, y]) } }
    }
}
final class Raster<P> : NonObjectiveCBase, RasterProtocol {
    typealias Pixel = P
    let width: Int
    let height: Int
    let rowBytes: Int
    let data: UnsafeMutablePointer<UInt8>
    init(width: Int, height: Int, rowBytes: Int) {
        precondition(width > 0 && height > 0 && rowBytes >= strideof(Pixel) * width)
        self.width = width
        self.height = height
        self.rowBytes = rowBytes
        data = UnsafeMutablePointer<UInt8>.init(allocatingCapacity: rowBytes * height)
    }
    deinit { data.deallocateCapacity(rowBytes * height) }
    func withEachPixel_2(inPlacePixelOp: @noescape (p: inout Pixel) -> Void) {
        for y in 0 ..< height { for x in 0 ..< width { inPlacePixelOp(p: &self[x, y]) } }
    }
}
let r = Raster<PxBGRAsRGB>.init(width: 240, height: 320, rowBytes: 240 * 4)
// Swift 3 regression? (Corresponding code worked in Xcode 7.3.1, Swift 2.2)
// r.withEachPixel { $0.a = 255 } // Error: Expression of type '()' is ambigous without more context
// r.withEachPixel { (p) in p.a = 255 } // Error: Expression of type '()' is ambigous without more context
//
// Workarounds for Swift 3, Xcode 8.0 beta (8S128d):
r.withEachPixel { (p: inout PxBGRAsRGB) in p.a = 255 } // OK
r.withEachPixel_2 { $0.a = 255 } // Also OK (when the method is declared in Raster instead of in RasterProtocol)

Working Swift 2.2 version for comparison:

struct PxBGRAsRGB {
    var b, g, r, a : UInt8
}
protocol RasterProtocol: class {
    associatedtype Pixel
    var width: Int { get }
    var height: Int { get }
    var rowBytes: Int { get }
    var data: UnsafeMutablePointer<UInt8> { get }
    subscript(x: Int, y: Int) -> Pixel { get set }
}
extension RasterProtocol {
    subscript(x: Int, y: Int) -> Pixel {
        get {
            precondition(x >= 0 && y >= 0 && x < width && y < height)
            return UnsafePointer<Pixel>(data.advancedBy(x * strideof(Pixel) + y * rowBytes)).memory
        }
        set {
            precondition(x >= 0 && y >= 0 && x < width && y < height)
            UnsafeMutablePointer<Pixel>(data.advancedBy(x * strideof(Pixel) + y * rowBytes)).memory = newValue
        }
    }
    func withEachPixel(@noescape inPlacePixelOp: (p: inout Pixel) -> Void) {
        for y in 0 ..< height { for x in 0 ..< width { inPlacePixelOp(p: &self[x, y]) } }
    }
}
final class Raster<P> : NonObjectiveCBase, RasterProtocol {
    typealias Pixel = P
    let width: Int
    let height: Int
    let rowBytes: Int
    let data: UnsafeMutablePointer<UInt8>
    init(width: Int, height: Int, rowBytes: Int) {
        precondition(width > 0 && height > 0 && rowBytes >= strideof(Pixel) * width)
        self.width = width
        self.height = height
        self.rowBytes = rowBytes
        data = UnsafeMutablePointer<UInt8>.alloc(rowBytes * height)
    }
    deinit { data.dealloc(rowBytes * height) }
}
let r = Raster<PxBGRAsRGB>.init(width: 240, height: 320, rowBytes: 240 * 4)
// This evidently works in Swift 2.2 (Xcode 7.3.1):
r.withEachPixel { $0.a = 255 } // OK
r.withEachPixel { (p) in p.a = 255 } // OK
@belkadan
Copy link
Contributor

Huh. I didn't remember this ever working reliably.

@jepers
Copy link
Author

jepers commented Jul 19, 2016

It (the short form) does work correctly in Xcode 7.3.1, Swift 2.2, and having to use the longer workaround-form in Swift 3 is frustrating …
Any progress/news on this?

@jepers
Copy link
Author

jepers commented Jul 19, 2016

Here is a smaller example demonstrating a similar and possibly related issue:

Xcode 7.3.1, Swift 2.2:

extension MutableCollectionType {
    mutating func processEachElementInPlace(op: (inout Generator.Element) -> Void) {
        for index in self.indices { op(&self[index]) }
    }
}
var a = [1, 2, 3, 4]
a.processEachElementInPlace { $0 *= 10 }
print(a) // Prints [10, 20, 30, 40]

Xcode 8 beta 3, Swift 3:

extension MutableCollection where Indices.Iterator.Element == Index {
    mutating func processEachElementInPlace(_ op: (inout Iterator.Element) -> Void) {
        for index in self.indices { op(&self[index]) }
    }
}
var a = [1, 2, 3, 4]
a.processEachElementInPlace { $0 *= 10 } // Error
print(a)

Error: Cannot invoke 'processEachElementInPlace' with an argument list of type '((inout Int) -> ())'

(Also, that where clause has to be there in order for the subscript to compile.)

@jepers
Copy link
Author

jepers commented Jul 25, 2016

One more shorter way of demonstrating the issue:
This code should compile without modification on both 2.2 and 3:

protocol P {
    associatedtype A
    var v: A { get set }
}
extension P {
    mutating func modifyInPlaceP(using op: (v: inout A) -> Void) {
        op(v: &self.v)
    }
}
struct S<T> : P { var v: T }
extension S {
    mutating func modifyInPlaceS(using op: (v: inout T) -> Void) {
        op(v: &self.v)
    }
}
struct Point { var x, y : Double }

var a = S.init(v: 1)

a.modifyInPlaceS { (v) in v = v * 2 } // OK
a.modifyInPlaceP { (v) in v = v * 2 } // OK

a.modifyInPlaceS { (v) in v *= 3 } // OK
a.modifyInPlaceP { (v) in v *= 3 } // OK in Swift 2.2, ERROR in Swift 3

var b = S.init(v: Point(x: 1, y: 2))

b.modifyInPlaceS { (v) in v.x = 4 } // OK
b.modifyInPlaceP { (v) in v.x = 4 } // OK in Swift 2.2, ERROR in Swift 3

@jepers
Copy link
Author

jepers commented Aug 4, 2016

Issue remains unsolved in dev snapshot 2016-08-16 (a).
Here's the last example again slightly modified for latest Swift syntax changes:

protocol P {
    associatedtype A
    var v: A { get set }
}
extension P {
    mutating func modifyInPlaceP(using op: (_: inout A) -> Void) {
        op(&self.v)
    }
}
struct S<T> : P { var v: T }
extension S {
    mutating func modifyInPlaceS(using op: (_: inout T) -> Void) {
        op(&self.v)
    }
}
struct Point { var x, y : Double }

var a = S.init(v: 1)

a.modifyInPlaceS { (v) in v = v * 2 } // OK
a.modifyInPlaceP { (v) in v = v * 2 } // OK

a.modifyInPlaceS { (v) in v *= 3 } // OK
a.modifyInPlaceP { (v) in v *= 3 } // OK in Swift 2.2, ERROR in Swift 3

var b = S.init(v: Point(x: 1, y: 2))

b.modifyInPlaceS { (v) in v.x = 4 } // OK
b.modifyInPlaceP { (v) in v.x = 4 } // OK in Swift 2.2, ERROR in Swift 3

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself regression swift 3.0 type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants