I think the direction the compile is erring here is still correct ( nonnilable).
(local) Variables are nonNilable unless explicitly declared or initialised to be nilable.
Yes (by implementation ) there is an area between implicit decl and overt use where the variable (reference) may have a nil value but it doesnt matter because the variable isnt being set till first use and cant be used in that window
since access before use will generate a diagnostic.
I would still say that this construct is a 'dont do this'.
For a construction like this it should be written with some form of (explicit) initialisation unconditionally so that there are no code paths that result in an unset (nil) variable before use.
class LessonVariableScopeAndType
def acceptsNonNilable(nonnilable as Object)
print "Enlignment achieved"
def main
# castMe implicitly declared here
castMe = NullObject() # 1) castMe unconditionally initialised to null object here.
if false
castMe = Object()
#without 1) castMe can be nil/unset here which breaks nilability invariant going on
.acceptsNonNilable(castMe)
Leaving aside nilability questions its the same issue as if the variable was primitive/value and could not ever be 0/unintialised.
def main
castMe as int # typed but not initialised
if false
castMe = 999
#invariant chk below
assert castMe <> 0 # not unintialised - can fail
.acceptsNonNilable(castMe)
wrt cobra, For correction two possibilities (sans full flow analysis)
1 Initialise all local nonnilable refs to an innocuous (compiler provided) static (non nil) Null Object (compiler gen unInitObject = Object())
cons - most of time a wasted assignment as first cobra code ref will set it to something sensible, performance impact.
2) Perhaps better
In one of the binding phases, walk the AST between (implicit) point of dcl and first use
outside subordinate blocks and emit a diagnostic if variable used without being set (noting that nonilable needs to be explicitly assigned before use for all codepaths)
This would detect the above caseand force correction (with explicit typing or an explicit correcting unconditional initial assignment)
Re charles note 1) checking unguarded nilable refs.
Some languages ( even with no notion of nil tracking) have a notion of a null checked deref (rather than doing it regardless and
throwing a NullRef at runtime)
It adds checks/boilerplate so that a nil reference returns nil for the expression rather than just barfing at runtime
( I vaguely recall this as named an 'elvis operator' tho thats perhaps more with data derefs, nil coalescing)
say ?. is the checked deref operator
foo.bar = nil # somehow
...
x = foo.bar?.baz # derefed
# executes fine but
assert x is nil # afterward
I dont see how this as particularly helpful though, as it seems its only deferring the point where a nil reference bites yo onna butt.
It does indicate tho that the writer was aware of the possibility of a nil in the deref chain