NRE from an "uninitialized this" in constructor

Hi there,

I’m relatively new to Kotlin, and its handling of nulls / prevention of NREs looks really cool. But I’m reading the section that describes the four specific scenarios where one can still get NREs, and I’m having trouble visualizing the last one:

“There’s some data inconsistency with regard to initialization (an uninitialized this available in a constructor is used somewhere)”

When would this happen?

class Foo {
    val c: String

    init {
        bar()
        c = ""
    }

    fun bar() {
        println(c.length)
    }
}
2 Likes

Ah, of course! Thank you for the example.

Is this a compiler bug or a conscious design decision?

The compiler is smart enough when we inline the bar function:

class Foo {
    val c: String

    init {
        // bar()
        println(c.length) // Compile-time error: Variable 'c' must be initialized
        c = ""
    }

    fun bar() {
        println(c.length)
    }

}

So why doesn’t the compiler check for uninitialized variables inside bar() ? Would it be too time-consuming?

1 Like

In the general case you can’t tell whether that is safe or not unfortunately.

If the code is inlined then as you say the compiler knows and tells you.

However if you call a method and that method gets overridden in a subclass you can’t really figure out if an access is safe or not.
This is because the 2 classes are compiled independently at different times.

However if you call a method and that method gets overridden in a subclass you can’t really figure out if an access is safe or not.

Class Foo cannot be subclassed as it isn’t open, so I don’t think your explanation applies to this case.

Here’s a modified version of that class, in which the NPE still happens:

private class Foo {
    private val c: String
    init {
        bar()
        c = ""
    }
    private inline fun bar() = c.length
}

Even with a final class it’s not always possible for the compiler to check all paths. You can have if-statements, call other functions, pass the this instance around, etc. Unless you want to restrict init to just assignments there are situtations where the compile won’t be able to check for NPEs.
The kotlin compiler therefor will only check the simplest situations and this means it won’t check functions called from init. Yes it is possible to write a better check for this, but this comes with performance issues and is quite complex. Maybe a stronger check can be added to kotlin at a later point but right now it does not exist and as far as I know is not planed. Also a full check won’t ever be possible unless all classes are final and compiled at the same time (which even if they are final they are not).
So to answer your earlier question, this is by design and not a compiler bug.

  1. Where compiler should report error, on call bar() on inside body of bar at c.length?
  2. Which function calls should be checked by compiler – only private inline? Actually there is no difference in that case between private inline and all others functions that have reference to leaking this (member or not)
  3. What if bar calls some other member function, that possibly access uninitialized c? Should compiler checks it’s body too?

Actually, as @Wasabi375 described, it’s possible to add such “cross-function” analysis, but it will lead to dramatically increasing of compilation time, because of each function call can run separate analysis of whole project, so it won’t be implemented in compiler. Current version of analysis works with O(LOC) asymptotic