0

In a Swift playground I wrote this line, for which the result will in most cases overflow:

var ran = UInt8(arc4random())

I do realize a more appropriate method would be to mask off the lower bits, or take a mod, but that's irrelevant to my question. What happens is that rather than there being an error noting the overflow (at least a warning?), the system attempts to run the code and produces an EXC_BAD_INSTRUCTION error. Am I overlooking something or is this a bug in the semantic analysis performed by Swift?

This is using Xcode version 7.0.1

(Later edit: this question differs from a similar one in that it's a simpler context (store only, not an arithmetic operation) for which there is no operator such as &+ to indicate the overflow is permissible. The answer posted is, however, comprehensive and for which thanks!)

Community
  • 1
  • 1
Feldur
  • 1,121
  • 9
  • 23
  • This type of errors are runtime, so you can say syntax is correct but in runtime it will crash. This mostly happens when you forcefully unwrap optional values if you safely unwrap using 'if' statement this won't happen. – Mukesh Thawani Oct 10 '15 at 18:54
  • Possible duplicate of [Integer overflow gives EXC\_BAD\_INSTRUCTION in Swift](http://stackoverflow.com/questions/31417588/integer-overflow-gives-exc-bad-instruction-in-swift) – jtbandes Oct 10 '15 at 19:38

1 Answers1

2

You can get a random byte from arc4random like this:

var num: UInt8 = 0
arc4random_buf(&num, sizeofValue(num))

But your question is why the error. As described in the documentation, the regular numeric operators and initializers perform overflow checking and throw an error at runtime:

You can use the overflow operators &+, &-, and &* to perform operations with overflow on numbers of the same type.

Or you can use these static functions from the IntegerArithmeticType protocol:

public static func addWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)
public static func subtractWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)
public static func multiplyWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)
public static func divideWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)
public static func remainderWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)

Finally, there are init(bitPattern:) and init(truncatingBitPattern:) which copy/truncate raw bits:

Int32(bitPattern: arc4random()) // types of the same size
UInt8(truncatingBitPattern: arc4random()) // types of different size
jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • I think you're saying that the runtime error is the as-designed behavior, and it's irrelevant what the specific error is. That said, it seems the overflow isn't anything that can be surrounded by a try/catch. Recognizing and ignoring that I'm using a highly contrived example, is it the case that in situations like this the expectation is that there would be an explicit range check before the type conversion, or am I still not understanding the language design? – Feldur Oct 10 '15 at 19:20
  • Yes. Swift considers overflow an error, unless you explicitly choose one of the overflow operators (`&+`, `&*`, etc.) or functions (`addWithOverflow`, `multiplyWithOverflow`, etc.) or a `bitPattern:` or `truncatingBitPattern:` initializer. – jtbandes Oct 10 '15 at 19:22
  • Another solution would be `let ran = UInt8(arc4random_uniform(256))`. – Martin R Oct 10 '15 at 21:39
  • Yeah. I felt a little uneasy about that because 256 isn't representible in UInt8, but I guess that's the point of an upper bound. – jtbandes Oct 10 '15 at 21:49