martmists
06/04/2026, 7:53 PMcontext(_: MyType) but the methods on it don't seem to resolve.Youssef Shoaib [MOD]
06/04/2026, 8:02 PMmartmists
06/04/2026, 8:04 PMcontext(ctx: MyType) fun thing() = ctx.thing() for each method on MyType now, and add imports for those methods everywhere?Youssef Shoaib [MOD]
06/04/2026, 8:08 PMmartmists
06/04/2026, 8:08 PMphldavies
06/04/2026, 8:08 PMfun doSomething {
with(contextOf<MyType>())
thing()
}Youssef Shoaib [MOD]
06/04/2026, 8:09 PMphldavies
06/04/2026, 8:10 PMcontext(ctx: MyType) inline fun <A> typeScope(f: MyType.() -> A): A = ctx.f() and then use typeScope { ... } to lift the context into a received within a given scopemartmists
06/04/2026, 8:10 PMYoussef Shoaib [MOD]
06/04/2026, 8:10 PMcontext(_: MyType)
fun doSomething() {
with(contextOf<MyType>())
thing()
}
but I imagine you probably want something like:
context(@With _: MyType)
fun doSomething() {
thing()
}
which is doable, but with hacky workaroundsphldavies
06/04/2026, 8:11 PMmartmists
06/04/2026, 8:12 PMyou could do something like ...Unfortunately due to the massive scope of this project it's probably not an option, I might just pin this specific submodule on a different kotlin version because context receivers were much easier to maintain this codebase with.
Youssef Shoaib [MOD]
06/04/2026, 8:15 PMcontext(_: Unit)
fun unit() {}
I was able to get auto complete just typing un without importing it, and choosing the autocomplete adds the import
Just a shot in the dark: maybe your IDE is out of date?Youssef Shoaib [MOD]
06/04/2026, 8:17 PMMyType types do you have? Is the problem adding the bridge methods, or importing them? I understand your frustration here, but hopefully there's some way to get the migration working (and then you never have to worry about it)martmists
06/04/2026, 8:25 PMYoussef Shoaib [MOD]
06/04/2026, 8:28 PMmartmists
06/04/2026, 9:50 PMScope.error(message) method, but that doesn't seem to resolve correctly, instead resolving to kotlin.errorYoussef Shoaib [MOD]
06/04/2026, 10:18 PMScope version (once you make a bridge version obviously), or it should error (because it's an ambigious overload)martmists
06/04/2026, 10:32 PMYoussef Shoaib [MOD]
06/04/2026, 10:37 PMkotlin.error is the first option to show up, expectedly, but then context(_: Unit) fun error shows up, and it imports when picked
Edit: it seems I'm actually not on a latest EAP, so maybe it's a regression. I'll switch to the newest and see what happens
Edit 2: It still works the same on latest EAP. Here's my IDEA info:
IntelliJ IDEA 2026.2 EAP
Build #IU-262.7132.23, built on June 4, 2026
Source revision: 88faa234249d6
Runtime version: 25.0.3+9-b496.62 aarch64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
JCEF version: 144.0.15-262-b37
Toolkit: sun.lwawt.macosx.LWCToolkit
macOS 26.5
Kotlin analyzer version: 2.4.20-dev-4439
Registry:
debugger.new.tool.window.layout=true
ide.experimental.ui=true
cache.folding.model.on.disk=false
kotlin.k2.only.bundled.compiler.plugins.enabled=false
freeze.reporter.profiling=false
kotlin.scripting.index.dependencies.sources=true
Kotlin: 262.7132.23-IJAlejandro Serrano.Mena
06/05/2026, 7:46 AMAlejandro Serrano.Mena
06/05/2026, 7:47 AMmartmists
06/05/2026, 9:41 AMembrace the way context parameters are supposed to be usedUnfortunately that's not an acceptable use case here, the only alternative would be to wait for intersection types and to use one of those as receiver somehow without using extension methods
Alejandro Serrano.Mena
06/05/2026, 9:49 AMthe only alternative would be to wait for intersection typeswhy? you can have multiple context arguments, but have them resolve to the same value. If you have a more specific example that you could share, that would be great; but in my investigation most cases that seemed to require intersections could be handled otherwise
martmists
06/05/2026, 9:59 AMLuaCoroutineScope context receiver with methods like continue, break, goto, but also references to upvalues, scope, etc. This keeps code clean since it can just be something like
context(LuaCoroutineScope)
suspend fun TValue<*>.luaToString() {
val meta = this.metatable
if (meta is TTable) {
val tostringMeta = meta["__tostring"]
if (tostringMeta !is TNil) {
val out = collectAsLuaScope {
tostringMeta.luaCall(listOf(this@luaToString))
}.first()
if (out !is TString) {
error_("'__tostring' must return a string")
}
return_ (out)
}
}
when (this) {
is TNil -> return_ (TString("nil"))
is TBoolean -> return_ (TString(if (value) "true" else "false"))
is TNumber<*> -> return_ (TString(value.toString()))
is TString -> return_ (this)
is TFunction -> return_ (TString("function: 0x${this.hashCode().toString(16)}"))
else -> {
var name = when (this) {
is TUserdata -> "userdata"
is TTable -> "table"
else -> "unknown"
}
if (meta is TTable) {
val nameMeta = meta["__name"]
if (nameMeta is TString) {
name = nameMeta.value
}
}
return_ (TString("$name: 0x${hashCode().toString(16)}"))
}
}
}
instead of having to do something like
context(ctx: LuaCoroutineScope)
suspend fun TValue<*>.luaToString() {
val meta = this.metatable
if (meta is TTable) {
val tostringMeta = meta["__tostring"]
if (tostringMeta !is TNil) {
val out = ctx.collectAsLuaScope {
tostringMeta.luaCall(listOf(this@luaToString))
}.first()
if (out !is TString) {
ctx.error_("'__tostring' must return a string")
}
ctx.return_(out)
}
}
when (this) {
is TNil -> ctx.return_(TString("nil"))
is TBoolean -> ctx.return_(TString(if (value) "true" else "false"))
is TNumber<*> -> ctx.return_(TString(value.toString()))
is TString -> ctx.return_(this)
is TFunction -> ctx.return_(TString("function: 0x${this.hashCode().toString(16)}"))
else -> {
var name = when (this) {
is TUserdata -> "userdata"
is TTable -> "table"
else -> "unknown"
}
if (meta is TTable) {
val nameMeta = meta["__name"]
if (nameMeta is TString) {
name = nameMeta.value
}
}
return_(TString("$name: 0x${this.hashCode().toString(16)}"))
}
}
}
Though this is one of the simpler examples so it may not seem like much effort, but with hundreds of functions it becomes very verbose and things like ctx.return_(...) signal control flow worse than a return_ (...)