Hello! Consider: ```class Context { val v: Int...
# language-proposals
p
Hello! Consider:
Copy code
class Context {
    val v: Int = 1
}

class X {

    val v: Int = 0

    fun Context.test() {
        println(v) // such cases are difficult for code readers. Suggest to issue warning if not explicitly specified this@... for them 
    }
}
d
Implementation of such warning will dramatically hurts the compilation performance The thing is that when compiler tries to resolve some call/property access it looks for suitable candidates in ordered list of scopes (so called "tower") and stops on first scope in which it finds some suitable candidate It allows to quickly stop the resolution if corresponding property/function was found somewhere in close scope (like in local scope or scope of closest implicit receiver) So to implement this warning compiler should always go through the whole tower, which will be incredibly slow Also what about other cases of such ambiguity? E.g. "local
x
vs member `x`" or "member
x
vs top-level `x`"? Should they be reported too?
BTW context receivers can partially address your issue Scopes of context receivers are located in tower after scope of receivers, so
X.v
will win
Copy code
class Context {
    val v: Int = 1 // (1)
}

class X {
    val v: Int = 2 // (2)

    fun Context.test_2() {
        v // resolved to (1)
    }

    context(Context)
    fun test_2() {
        v // resolved to (2)
    }
}
In presence of context receivers tower looks like this (simplified): • scope of function extension receiver • scope of function dispatch receiver (
this
of parent class) • scopes of context receivers
p
Locals usually visible by reader. For members and globals it is not so good as with locals, especially when members located in super types. But with mix of receivers such cases become less readable. Reader must enter each scope (or ctrl-click) to find actual candidate. In case of such warning he can determine with less effort for what scope this member belongs to.
l
> ...warning... > and this totally destroys the beauty of DSL.
Copy code
buildJsonObject {
    literal("a", "ObjectBuilder")

    array("b") {
        literal("ArrayBuilder")
    }
}
Real world example speaks more.
p
Could you provide how is 'literal' defined?
l
One for JsonObjectBuilder with key, one for JsonArrayBuilder without.
A proper
@DslMarker
annotation is all we need.
p
It looks like that this is not the case I'm talking about.
l
Copy code
class Outer {
    val a: Int

    inner class Inner {
        val a: Int

        fun foo() {
            a // Will you warn?
        }
    }
}
p
In such scenario there will be no warning.
l
> In presence of context receivers tower looks like this (simplified): > • scope of function extension receiver > • scope of function dispatch receiver (
this
of parent class) > • scopes of context receivers I don't think it's a fault to the language. After someone learns how the resolution happens, it's not a problem anymore. It's consistent.
👍 1
p
It is not the fault, just additional difficulty for readers. No matter about their experiance.
For example i've created class T and use it as function extention receiver for many member functions in different classes. After some time i add function f() to T. After that i can break some logic in any class that have function f() and member with extention receiver T, cause now it will be resolved to T.f(). With warning this will be catched at compile time.
l
This is costly I suppose.
You'd now have to search for all scopes for all symbols, which I don't think is good for compiler.
p
For readers:
Copy code
class T{
  val x: Int
  fun test Context.() {
    println(x) // while reading code you have to go to Context declaration to check if it has or not x member
  }
}
I thought that compiler is for making programmer life easer.
l
If you don't know Context, learn it first. If clashes confuse you, all the members from Context will confuse you too because they don't exist in T.
1
p
You can't keep in the head everything. For more complex code you'll spend more time undestanding it.
Especially when working in team.
l
Then why would you use receiver? Just forbid it and everyone's happy.
p
But i like to use it. With this proposal their usage will be more attractive.
l
If this is for kotlin IDEA plugin, I could accept, but I don't want my compilation slows down again.
p
I suggest compiler command line flag.