[swift unboxed]

Safely unboxing the Swift language & standard library


Open-source Swift: Booleans

A close reading of the source to the black-and-white cookie of the standard library: Booleans.

4 December 2015 ∙ Standard Library ∙ written by

Last week I wrote about the BooleanType protocol. Now that we have open-source Swift, it’s time to look at the mother of all BooleanType types: Bool itself!

Close reading

Rather than the usual computer science or physics or philosophy background of most developers, I was an English major. We spent a good amount of time doing close readings of prose passages and poetry.

I thought, why not put all that education to good use and try something similar for code?

After all — Prose : Documentation :: Poetry : Code, right?

Bool.swift

The code is all inline below, but you can see the original file in the Swift repository too: Bool.swift.

I picked a seemingly simple example to start as a good way into the Swift standard library source code for me. Let’s get started!

/// A value type whose instances are either `true` or `false`.
public struct Bool {

No surprise, it’s a struct. I still hear from many people who are amazed at the heavy use of value types, but Swift is massively tilted in that direction: structs outnumber classes by a factor of 20 in the standard library.

Storage

internal var _value: Builtin.Int1

Here’s the actual storage: a one-bit integer. We’ll presumably get 1 for true and 0 for false.

Builtin means the thing is coming from “one layer down”. If you’ve looked into SIL, you’ll see this all over the place when it digs down to the LLVM layer.

In this case, Int1 should map to the LLVM type of the same name further down the line.

In terms of style, they’re using a leading underscore for non-public properties. Also, note that the property is internal rather than private; that means other modules in the standard library can access this bit directly when needed.

Initializers

/// Default-initialize Boolean value to `false`.
@_transparent
public init() {
  let zero: Int8 = 0
  self._value = Builtin.trunc_Int8_Int1(zero._value)
}

Here’s the public initializer. By default, Bool instances get a default value of false.

There are some minor acrobatics here to get a zero value into the _value property. While Swift has a native Int8 to go with the Builtin.Int8, there’s no native Int1.

To get that one-bit zero, the strategy here is to start with an Int8. From there, there’s another handy Builtin to truncate the 8-bit value to a 1-bit value.

Of course trunc_Int8_Int1 takes a Builtin.Int8 rather than a Swift Int8. As with Bool, Int8 has a _value property to access the raw built-in value. And since it’s internal rather than private, we have access to it here!

What’s @_transparent? Here’s a note from the documentation:

Semantically, @_transparent means something like “treat this operation as if it were a primitive operation”. The name is meant to imply that both the compiler and the compiled program will “see through” the operation to its implementation.

Check out the docs for the gory details, but think of @_transparent functions as unchanging inlined functions that don’t rely on private or internal details.

@_transparent
internal init(_ v: Builtin.Int1) { self._value = v }

Here’s the second initializer, with a more common structure: it takes a Builtin.Int1 and assigns it directly to the _value property. This initializer is internal-only, which makes sense since only the standard library code will have easy access to Builtin.Int1 values anyway.

BooleanLiteralConvertible

extension Bool : _BuiltinBooleanLiteralConvertible, BooleanLiteralConvertible {
  @_transparent
  public init(_builtinBooleanLiteral value: Builtin.Int1) {
    self._value = value
  }

  /// Create an instance initialized to `value`.
  @_transparent
  public init(booleanLiteral value: Bool) {
    self = value
  }
}

Bool conforms to BooleanLiteralConvertible and its more private counterpart _BuiltinBooleanLiteralConvertible.

Nothing too surprising here: the protocols have required initializers and they’re implemented here: one that takes a literal Builtin.Int1 and the other that takes a Bool.

There’s some duplication here since we’ve already seen an initializer that takes a Builtin.Int1 value. They have different external parameter names and one is internal vs public, but the implementations are identical.

BooleanType

extension Bool : BooleanType {
  @_transparent
  @warn_unused_result
  public func _getBuiltinLogicValue() -> Builtin.Int1 {
    return _value
  }

  /// Identical to `self`.
  @_transparent public var boolValue: Bool { return self }

  /// Construct an instance representing the same logical value as `value`.
  public init<T : BooleanType>(_ value: T) {
    self = value.boolValue
  }
}

Bool should, of course, conform to BooleanType, which has one required property: boolValue. That needs to return a Bool which makes the implementation here very simple: return self.

There are two additional things here in the extension:

  • _getBuiltinLogicValue() — returns that internal Builtin.Int1 value of the Bool, which seems to break encapsulation. I can only find two instances where the method is called in the standard library code, both in the set of SIL tests.

  • Another initializer that takes anything that conforms to BooleanType. That makes sense: you should be able to initialize a Bool from any such BooleanType.

@warn_unused_result makes its first appearance. That’s what triggers the “Result of call is unused” warning you see in Xcode.

CustomStringConvertible

extension Bool : CustomStringConvertible {
  /// A textual representation of `self`.
  public var description: String {
    return self ? "true" : "false"
  }
}

Playground users and people debugging their code all around the world will love you forever if you remember to implement CustomStringConvertible for your types.

Magic conversion

// This is a magic entrypoint known to the compiler.
@_transparent
public // COMPILER_INTRINSIC
func _getBool(v: Builtin.Int1) -> Bool { return Bool(v) }

It’s magic!

Intrinsic functions are indeed magic since the compiler will have special knowledge of them.

In this case, the function is a shortcut to turn one of those Builtin.Int1 values into a Bool using the initializer we’ve already seen.

This function is @_transparent and will be inlined away—as will the initializer, come to think of it. It’s inlining all the way down!

Equatable & Hashable

@_transparent
extension Bool : Equatable, Hashable {
  public var hashValue: Int {
    return self ? 1 : 0
  }
}

Best practice for value types is to make them Equatable and Hashable.

For Equatable, you need to provide an implementation for ==, which you’ll see in a bit.

For Hashable, you need to hash the value down to an Int. That’s easy in this case: true is 1 and false is 0.

Unary logical complement

@_transparent
@warn_unused_result
public prefix func !(a: Bool) -> Bool {
  return Bool(Builtin.xor_Int1(a._value, true._value))
}

We’re coming to the end, which must mean: more public functions!

Here’s the ! operator to turn true into false and vice versa. The obvious solution to toggle the bit might be something like this:

return a ? false : true

But think back to bitwise operations instead. How do you toggle a bit? XOR it with 1, of course! That’s what’s happening here: xor_Int1 with the argument’s value and the Int1 representation of true.

I guess when you go way down to assembly, this will become a plain old XOR instruction, which is super fast. Contrast that to the obvious implementation, which has a branch.

Trivia note from the early x86 days: XOR'ing a register with itself (XOR AX, AX) was the fastest way to zero out the value since the instruction was shorter than MOV AX, 0.

Equatable

@_transparent
@warn_unused_result
public func ==(lhs: Bool, rhs: Bool) -> Bool {
  return Bool(Builtin.cmp_eq_Int1(lhs._value, rhs._value))
}

Here’s the == operator to make Equatable happy.

Again, the implementation uses a built-in to compare the Int1 values directly. Perhaps this will compile down to a CMP in assembly to again be super fast?

Final thoughts

That was a lot of words for a relatively simple type!

Lifting up the covers of a fundamental type such as Bool offers plenty of coding tips and implementation details. Some highlights:

  • Two-space tabs — That’s spaces, not tabs. I feel like I would fit right into the Swift team in case you’re reading, Apple. ;)

  • Protocols — Type functionality is split up by protocol to keep things organized.

  • Extensions — Each protocol conformance is in its own extension. I’ve used that style since the Objective-C days and am glad to see it here too.

  • Built-ins — In contrast to C, Swift is more levels above the metal: in the LLVM workflow, you’re going from Swift to SIL to LLVM IR to machine code. Built-ins are a neat way to move down one level and let a lower-level implementation know your intentions so it can optimize accordingly.

There’s what I like best about open-source: getting below the surface and seeing how things work.

I hope this was useful and a fun read! Share, let me know what you think, and in the meantime I plan to keep poking around the source.