Circular references allowed at top and static levels?

This one is pretty simple. Both at the top-level:

class Reference { }

let z: Reference = z

print(z) // EXC_BAD_ACCESS

and static levels:

enum Main {
    
    static let z: Int = z
    
}

print(Main.z) // EXC_BAD_ACCESS

I didn't expect this to compile at all, but it does, even though it crashes when run. Are there any instances where this wouldn't be a bug and this syntax should be allowed? Most of the time this (in my opinion) correctly doesn't work:

Top-level without explicit type annotation:

let g = g // error: "Circular reference"

Function-scope:

func y() {
    class Reference { }
    
    let z: Reference = z // error: Use of local variable 'z' before its declaration
}

This is a known bug that’s a bit trickier than just comparing the LHS and RHS of assignment, e.g:

func f() -> [Int] {
  x
}
let x: [Int] = f()
print(x)
2 Likes

Huh. Interesting. Is there a reason why the obvious instances aren't checked for?

We do check for this correctly in local context:

func outer() {
  func f() -> [Int] {   // error: closure captures 'x' before it is declared
    x   // note: captured here
  }
  let x: [Int] = f()
  print(x)
}

There is no good reason, no. ;-)

However, with module-scope globals (not script mode globals), we cannot completely check for the absence of circularity at compile time, because of stuff like this:

func complicatedFunction1() {
  ...
  if someCondition {
    print(global2)
  }
  ...
}

func complicatedFunction2() {
  ...
  if someOtherCondition {
    print(global1)
  }
  ...
}

let global1 = complicatedFunction1()
let global2 = complicatedFunction2()
3 Likes