Lateinit var for inline methods


#1

If I have an inline method, and I make a constructor assignment within a lambda passed to that inline method. Why does the variable have to be lateinit? If the lamba isn’t marked as noinline or crossinline, isn’t that safe to be private val bar: Bar instead of private lateinit var bar: Bar?

class A {
    private lateinit var bar: Bar // Why does this need to be lateinit?
    init {
        foo {
            bar = Bar()
        }
    }
}
inline fun foo(init: ()->Unit) {
    init()
}

#2

In your example this would be save, but there is no reasonable way for the compiler to know that. There is no guarantee that foo will call init. I mean yeah sure, you could do some sort of static analysis but this is a problem which if I remember correctly is proven to be not solvable on a turing machine.


#3

I thought inline functions had control flow analysis.


#4

Currently inline functions are implemented through the bytecode of the function being mangled for inline use. The ast is lost at that point.


#5

Really? That doesn’t seem right. Surely determining whether a codepoint is hit exactly once in all non-Nothing cases isn’t an unsolvable problem in the general. Though I guess once you start recursively passing it around it might be. Well you could always have a keyword “once” for lambdas and have it so that a function that takes a “once” lambda either consumes the lambda for all cases or passes it to another “once” function that consumes it. At that point you’re pretty much just mimicking val behavior when it comes to verifying at or before compile time.


#6

There’s actually an interesting construct found in the stdlib source code, where inline functions with functions as parameters define a contract. Something like:

contract {
  callsInPlace = true
}

or something like that. The contract method is an inlined function with no code in it, so it is not actually changing anything at runtime, though the compiler will be able to analyze it at compile time.
There are a few more variables that can be set, I don’t know exactly what they are called or what the function is, but one application is that you can use this to tell the compiler that the function parameter is used one time synchronously, every time.

Unfortunately, this construct is internal.


#7

JB: Any chance there are plans to make that contract metadata public?


#8

Although right now the contract stuff doesn’t actually do what I was hoping:

private val t: String

init {
	3.apply {
		t = "" // Error, captured member values initialization is forbidden due to possible reassignment
	}
}

This does look descriptive of what I want though!

contract {
	callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}