[pitch] More lenient (yet safe) rules about referencing `self` in `init` before `super.init`

Currently, you are totally disallowed from referring to self in init (except when referring to an already-initialized instance variable, or when assigning to self) before a call to super.init. This is overly restrictive. It should be possible -- because it is safe -- to reference self before super.init as long as a) the use comes after the object has fully initialized all of the instance variables it introduced, and b) the compiler can prove that the use of self only involves functions and/or computed properties that only involve those instance variables. For instance, the following does not compile due to the use of lengthOfX before super.init. However the compiler should have no trouble proving that lengthOfX only makes use of instance variable(s) introduced by B itself, and hence that this is perfectly safe.

class A {
	init() {}

class B: A {
	var x: String
	var lengthOfX: Int { return x.count }
	init(x: String) {
		self.x = x
		print(self.lengthOfX) // error: 'self' used in property access 'lengthOfX' before 'super.init' call

A more salient example would involve passing a value to super.init, where the value is calculated from instance properties.

What if A comes from another module, of which the source is not available? A might have private properties that are not set until super.init() is called, and calling .lengthOfX might access other values implemented by means of A's internals...
I think it might be possible to do these kind of checks, but I don't think it's easy at all...

I'm not arguing that the compiler should need to become super intelligent. But for same-module code, and especially same-file code, this seems relatively straightforward.

This would also be limited to non-open classes otherwise there may be subclasses that can override lengthOfX

1 Like

While intra-module analysis could help solve issues like this, it is worth noting that changing implementation details of a function that is present somewhere in the call tree of an initializer could then cause compilation errors in that initializer, far removed from the actual code change.

Better add those as constraints in the function signature telling you it only depends on self.length. The function signature acting as a contract will make it obvious who is in the wrong if lenghtOfX in your example is later changed and touches another variable. Then, you can keep contract the same and the error is in lengthOfX, or you change the contract and the initializer is in error.

I'm not sure it's really worth adding this to function signatures just to make initializers easier to write though.