Recursive type issue

class RecursiveType
{
   var s    :String
   var link :RecursiveType?
   
   init( _ s:String, _ link:RecursiveType? = nil )
   {
      self.s      = s
      self.link   = link
   }
}

// Create a linked list of RecursiveType objects:

var rt3  = RecursiveType( "Last node", rt3 )

This last statement produces an error ("Circular reference"). While it is nice that Swift catches this and warns you about the problem, I think that making this an error (as opposed to just a warning) is a bit harsh.

Rather than just file a bug report on this, I thought I'd check in and see if there is some fundamental reason for making this an error rather than a warning. FWIW, it's not that hard to create cycles if you really want them, for example, you get away with this:

var rt3  = RecursiveType( "Last node", nil )
rt3.link = rt3

Just wondering...

The "circular reference" being diagnosed here isn't the runtime reference cycle, but the reference to rt3 before it's actually been assigned by the larger statement. I believe the error is emitted by the type checker when it tries to compute the type of var rt3, which requires computing the type of the expression on the right hand side, which again attempts to look up the type of rt3.

Incidentally, if you explicitly assign a type to rt3, you get a better error message:

func f() {
  var rt3: RecursiveType = RecursiveType( "Last node", rt3 )
  // error: use of local variable 'rt3' before its declaration
}

Note that if you try this in top-level code, it actually compiles successfully, but the value of rt3 on the right hand side isn't going to be what you want. This is a bug with how global variables are treated in top-level code.

6 Likes

If you have an assignment expression like x = foo(x), we first evaluate the right-hand side, to get the new value to store to the location on the left-hand side. But when the right-hand side references that same storage location, the load of x on the right-hand side happens before the store to x. Now when you combine a declaration with an assignment, like var x = foo(x), you’re effectively loading from an uninitialized memory location.

5 Likes