Law of Exclusivity runtime false positive?

Hello,

In Xcode 9 beta 4, Swift 4, I’m getting runtime errors popping up for Simultaneous accesses and I think they may be false negatives. Here’s a stripped down version of my code:

class MyButton: UIButton {
  fileprivate var imageRect: CGRect = .zero
  fileprivate var titleRect: CGRect = .zero

        func updateRects() {
            if imageBeforeTitle {
                layoutHorizontally(leftRect: &imageRect, rightRect: &titleRect)
            } else {
                layoutHorizontally(leftRect: &titleRect, rightRect: &imageRect)
            }
        }

        func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
            leftRect.origin.x = padding
            rightRect.origin.x = leftRect.maxX + spacing
        }
}

While the layoutHorizontally method has two CGRect inout parameters, never in my code do I pass the same CGRect. Any ideas if this is a bug I should post on bugs.swift.org or if I’m missing something?

David.

Hi David,

Are padding or spacing computed properties that access either rectangle? They aren't defined in your stripped down code. For example:

var spacing: CGFloat {
    return imageRect.maxX
}

Without doing something like that, I don't see the same access conflicts that you're seeing.

-Kyle

···

On Jul 27, 2017, at 5:04 AM, David Hart via swift-users <swift-users@swift.org> wrote:

Hello,

In Xcode 9 beta 4, Swift 4, I’m getting runtime errors popping up for Simultaneous accesses and I think they may be false negatives. Here’s a stripped down version of my code:

class MyButton: UIButton {
  fileprivate var imageRect: CGRect = .zero
  fileprivate var titleRect: CGRect = .zero

        func updateRects() {
            if imageBeforeTitle {
                layoutHorizontally(leftRect: &imageRect, rightRect: &titleRect)
            } else {
                layoutHorizontally(leftRect: &titleRect, rightRect: &imageRect)
            }
        }

        func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
            leftRect.origin.x = padding
            rightRect.origin.x = leftRect.maxX + spacing
        }
}

While the layoutHorizontally method has two CGRect inout parameters, never in my code do I pass the same CGRect. Any ideas if this is a bug I should post on bugs.swift.org <Issues · apple/swift · GitHub; or if I’m missing something?

David.
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Hello,

Indeed, I had reduced the code too much. John McCall was kind enough to have a look and here’s the offending code:

func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
    let totalWidth = imageRect.size.width + titleRect.size.width + contentSpacing
    rightRect.origin.x = leftRect.maxX + contentSpacing
}

The problem is that imageRect and titleRect are referenced both directly and in the inout parameters.

But there’s something I’m not understanding. I went back to the ownership manifesto and re-read the law of exclusivity to make sure I understand it:

If a storage reference expression evaluates to a storage reference that is implemented by a variable, then the formal access duration of that access may not overlap the formal access duration of any other access to the same variable unless both accesses are reads.

So I tried to write a test to trigger the runtime error, but was unsuccessful:

class MyClass {
    var p: CGRect = .zero
}

func trigger(a: MyClass, b: MyClass) {
    a.p = b.p
}

let m = MyClass()
trigger(a: m, b: m)

Here, a storage reference expression (a.p) evaluates to a storage reference that is implemented by a variable (the p property of an instance m of MyClass) and its formal access duration (the trigger function) overlaps the formal access duration of another access to the same variable (through the b.p storage reference expression) and both accesses are not reads (a.p is on the LHS of an assignment).

Why does this not trigger the Law of Exclusivity?

Regards,
David.

···

On 27 Jul 2017, at 21:33, Kyle Murray <kyle_murray@apple.com> wrote:

Hi David,

Are padding or spacing computed properties that access either rectangle? They aren't defined in your stripped down code. For example:

var spacing: CGFloat {
    return imageRect.maxX
}

Without doing something like that, I don't see the same access conflicts that you're seeing.

-Kyle

On Jul 27, 2017, at 5:04 AM, David Hart via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hello,

In Xcode 9 beta 4, Swift 4, I’m getting runtime errors popping up for Simultaneous accesses and I think they may be false negatives. Here’s a stripped down version of my code:

class MyButton: UIButton {
  fileprivate var imageRect: CGRect = .zero
  fileprivate var titleRect: CGRect = .zero

        func updateRects() {
            if imageBeforeTitle {
                layoutHorizontally(leftRect: &imageRect, rightRect: &titleRect)
            } else {
                layoutHorizontally(leftRect: &titleRect, rightRect: &imageRect)
            }
        }

        func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
            leftRect.origin.x = padding
            rightRect.origin.x = leftRect.maxX + spacing
        }
}

While the layoutHorizontally method has two CGRect inout parameters, never in my code do I pass the same CGRect. Any ideas if this is a bug I should post on bugs.swift.org <Issues · apple/swift · GitHub; or if I’m missing something?

David.
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

`b.p` is loaded before `a.p` is written to, so the accesses do not overlap even when a === b. Something like swap(&a.p, &b.p) (using the Swift 3 global definition of 'swap') would trigger an exclusivity trap when a === b, since the access to storage passed as an inout argument needs to last for the duration of the call.

-Joe

···

On Jul 28, 2017, at 12:06 AM, David Hart via swift-users <swift-users@swift.org> wrote:

Hello,

Indeed, I had reduced the code too much. John McCall was kind enough to have a look and here’s the offending code:

func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
    let totalWidth = imageRect.size.width + titleRect.size.width + contentSpacing
    rightRect.origin.x = leftRect.maxX + contentSpacing
}

The problem is that imageRect and titleRect are referenced both directly and in the inout parameters.

But there’s something I’m not understanding. I went back to the ownership manifesto and re-read the law of exclusivity to make sure I understand it:

If a storage reference expression evaluates to a storage reference that is implemented by a variable, then the formal access duration of that access may not overlap the formal access duration of any other access to the same variable unless both accesses are reads.

So I tried to write a test to trigger the runtime error, but was unsuccessful:

class MyClass {
    var p: CGRect = .zero
}

func trigger(a: MyClass, b: MyClass) {
    a.p = b.p
}

let m = MyClass()
trigger(a: m, b: m)

Here, a storage reference expression (a.p) evaluates to a storage reference that is implemented by a variable (the p property of an instance m of MyClass) and its formal access duration (the trigger function) overlaps the formal access duration of another access to the same variable (through the b.p storage reference expression) and both accesses are not reads (a.p is on the LHS of an assignment).

Why does this not trigger the Law of Exclusivity?

Is is possible that the compiler optimized away the call to trigger because it reasoned that you were not really changing any state at all? Because the trigger function is making an assignment of input to output, and the call instance is asking to make a copy of itself, the compiler could reason that after the call to trigger nothing really changes and there are no side effects, thus, no need to generate the function call instance.

Jonathan

···

On Jul 28, 2017, at 3:06 AM, David Hart via swift-users <swift-users@swift.org> wrote:

Hello,

Indeed, I had reduced the code too much. John McCall was kind enough to have a look and here’s the offending code:

func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
    let totalWidth = imageRect.size.width + titleRect.size.width + contentSpacing
    rightRect.origin.x = leftRect.maxX + contentSpacing
}

The problem is that imageRect and titleRect are referenced both directly and in the inout parameters.

But there’s something I’m not understanding. I went back to the ownership manifesto and re-read the law of exclusivity to make sure I understand it:

If a storage reference expression evaluates to a storage reference that is implemented by a variable, then the formal access duration of that access may not overlap the formal access duration of any other access to the same variable unless both accesses are reads.

So I tried to write a test to trigger the runtime error, but was unsuccessful:

class MyClass {
    var p: CGRect = .zero
}

func trigger(a: MyClass, b: MyClass) {
    a.p = b.p
}

let m = MyClass()
trigger(a: m, b: m)

Here, a storage reference expression (a.p) evaluates to a storage reference that is implemented by a variable (the p property of an instance m of MyClass) and its formal access duration (the trigger function) overlaps the formal access duration of another access to the same variable (through the b.p storage reference expression) and both accesses are not reads (a.p is on the LHS of an assignment).

Why does this not trigger the Law of Exclusivity?

Regards,
David.

On 27 Jul 2017, at 21:33, Kyle Murray <kyle_murray@apple.com <mailto:kyle_murray@apple.com>> wrote:

Hi David,

Are padding or spacing computed properties that access either rectangle? They aren't defined in your stripped down code. For example:

var spacing: CGFloat {
    return imageRect.maxX
}

Without doing something like that, I don't see the same access conflicts that you're seeing.

-Kyle

On Jul 27, 2017, at 5:04 AM, David Hart via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hello,

In Xcode 9 beta 4, Swift 4, I’m getting runtime errors popping up for Simultaneous accesses and I think they may be false negatives. Here’s a stripped down version of my code:

class MyButton: UIButton {
  fileprivate var imageRect: CGRect = .zero
  fileprivate var titleRect: CGRect = .zero

        func updateRects() {
            if imageBeforeTitle {
                layoutHorizontally(leftRect: &imageRect, rightRect: &titleRect)
            } else {
                layoutHorizontally(leftRect: &titleRect, rightRect: &imageRect)
            }
        }

        func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
            leftRect.origin.x = padding
            rightRect.origin.x = leftRect.maxX + spacing
        }
}

While the layoutHorizontally method has two CGRect inout parameters, never in my code do I pass the same CGRect. Any ideas if this is a bug I should post on bugs.swift.org <Issues · apple/swift · GitHub; or if I’m missing something?

David.
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Hello,

Indeed, I had reduced the code too much. John McCall was kind enough to have a look and here’s the offending code:

func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
    let totalWidth = imageRect.size.width + titleRect.size.width + contentSpacing
    rightRect.origin.x = leftRect.maxX + contentSpacing
}

The problem is that imageRect and titleRect are referenced both directly and in the inout parameters.

But there’s something I’m not understanding. I went back to the ownership manifesto and re-read the law of exclusivity to make sure I understand it:

If a storage reference expression evaluates to a storage reference that is implemented by a variable, then the formal access duration of that access may not overlap the formal access duration of any other access to the same variable unless both accesses are reads.

So I tried to write a test to trigger the runtime error, but was unsuccessful:

class MyClass {
    var p: CGRect = .zero
}

func trigger(a: MyClass, b: MyClass) {
    a.p = b.p
}

let m = MyClass()
trigger(a: m, b: m)

Here, a storage reference expression (a.p) evaluates to a storage reference that is implemented by a variable (the p property of an instance m of MyClass) and its formal access duration (the trigger function) overlaps the formal access duration of another access to the same variable (through the b.p storage reference expression) and both accesses are not reads (a.p is on the LHS of an assignment).

Why does this not trigger the Law of Exclusivity?

`b.p` is loaded before `a.p` is written to, so the accesses do not overlap even when a === b. Something like swap(&a.p, &b.p) (using the Swift 3 global definition of 'swap') would trigger an exclusivity trap when a === b, since the access to storage passed as an inout argument needs to last for the duration of the call.

Thanks for the explanation! It’s starting to make sense :) Is it possible to trigger the exclusivity trap without an inout argument?

···

On 28 Jul 2017, at 18:55, Joe Groff <jgroff@apple.com> wrote:

On Jul 28, 2017, at 12:06 AM, David Hart via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

-Joe

Users are probably more likely to run into problems with mutating methods, which are implicitly `inout`:

struct S {
  var x = 1

  mutating func updateX(_ getOtherS: ()->S) {
    x += getOtherS().x
  }
}

class C {
  var s = S()
}

let c = C()
c.s.updateX( { c.s } )

-Andy

···

On Jul 30, 2017, at 5:11 AM, David Hart via swift-users <swift-users@swift.org> wrote:

On 28 Jul 2017, at 18:55, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Jul 28, 2017, at 12:06 AM, David Hart via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hello,

Indeed, I had reduced the code too much. John McCall was kind enough to have a look and here’s the offending code:

func layoutHorizontally(leftRect: inout CGRect, rightRect: inout CGRect) {
    let totalWidth = imageRect.size.width + titleRect.size.width + contentSpacing
    rightRect.origin.x = leftRect.maxX + contentSpacing
}

The problem is that imageRect and titleRect are referenced both directly and in the inout parameters.

But there’s something I’m not understanding. I went back to the ownership manifesto and re-read the law of exclusivity to make sure I understand it:

If a storage reference expression evaluates to a storage reference that is implemented by a variable, then the formal access duration of that access may not overlap the formal access duration of any other access to the same variable unless both accesses are reads.

So I tried to write a test to trigger the runtime error, but was unsuccessful:

class MyClass {
    var p: CGRect = .zero
}

func trigger(a: MyClass, b: MyClass) {
    a.p = b.p
}

let m = MyClass()
trigger(a: m, b: m)

Here, a storage reference expression (a.p) evaluates to a storage reference that is implemented by a variable (the p property of an instance m of MyClass) and its formal access duration (the trigger function) overlaps the formal access duration of another access to the same variable (through the b.p storage reference expression) and both accesses are not reads (a.p is on the LHS of an assignment).

Why does this not trigger the Law of Exclusivity?

`b.p` is loaded before `a.p` is written to, so the accesses do not overlap even when a === b. Something like swap(&a.p, &b.p) (using the Swift 3 global definition of 'swap') would trigger an exclusivity trap when a === b, since the access to storage passed as an inout argument needs to last for the duration of the call.

Thanks for the explanation! It’s starting to make sense :) Is it possible to trigger the exclusivity trap without an inout argument?

-Joe