Skip to content

Commit 7987a0c

Browse files
authored
Fix inline export forwarder generation regression (#23126)
To explain what was happening and what the issue was: Minimalisation: ```scala import scala.quoted.* package jam { trait JamCoreDsl { implicit inline def defaultJamConfig: this.JamConfig = new JamConfig(brewRecRegex = ".*") class JamConfig(val brewRecRegex: String) inline def brew(implicit inline config: JamConfig): Unit = ??? } private object internal extends JamCoreDsl export internal._ } object test { jam.brew } ``` would fail retyping in the inlining phase. During typer, because we were exporting contents of `Internal`, the compiler created forwarders (in Namer.addForwarder) for methods chosen in the export. The forwarders looked like this: ```scala final implicit inline def defaultJamConfig: jam.internal.JamConfig = jam.Internal.defaultJamConfig final inline def brew(implicit inline config: jam.internal.JamConfig): Unit = jam.Internal.brew(config) ``` While creating them, the compiler would also try to type them. Because those forwarder methods were inline, the compiler would call PrepareInlineable.makeInlineable on them. That method would transform rhs of the forwarder method, to make sure it does not reference any private methods/objects. Since `Internal` is private (and referenced in both exported methods, notably in brew with rhs jam.Internal.brew(config)), an accessor inline$Internal was created, and the forwarder method contents remapped. Then, during inlining in the inline phase, the ReTyper was not able to type `jam.inline$internal.brew(config)`, with `jam.inline$internal.brew` being typed as `Nothing => Unit` (likely having remapped the argument to Nothing in an asSeenFrom, after deciding that the prefix is not stable). But none of this happened when I created those forwarders and accessors manually. After some digging it turned out that the `inline$internal` accessor method was typed as `TypeRef(ThisType(TypeRef(NoPrefix,module class jam)),module class internal$))`, but the manually created one was a `TermRef(ThisType(TypeRef(NoPrefix,module class jam)), object Internal)`. It seems like the first one has an unstable prefix, so its denotation is not typed correctly, leading to the issue. Fixes #22593
2 parents 001eb6d + f791837 commit 7987a0c

File tree

4 files changed

+52
-1
lines changed

4 files changed

+52
-1
lines changed

compiler/src/dotty/tools/dotc/transform/AccessProxies.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,13 @@ abstract class AccessProxies {
141141
if accessorClass.is(Package) then
142142
accessorClass = ctx.owner.topLevelClass
143143
val accessorName = accessorNameOf(accessed.name, accessorClass)
144+
val mappedInfo = accessed.info match
145+
// TypeRef pointing to module class seems to not be stable, so we remap that to a TermRef
146+
// see test i22593.scala (and issue #i22593)
147+
case tref @ TypeRef(prefix, _) if tref.symbol.is(Module) => TermRef(prefix, tref.symbol.companionModule)
148+
case other => other
144149
val accessorInfo =
145-
accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner)
150+
mappedInfo.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner)
146151
val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed)
147152
rewire(reference, accessor)
148153
}

tests/pos/i22593a.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.quoted.*
2+
3+
package jam {
4+
trait JamCoreDsl {
5+
implicit inline def defaultJamConfig: this.JamConfig =
6+
new JamConfig(brewRecRegex = ".*")
7+
class JamConfig(val brewRecRegex: String)
8+
inline def brew(implicit inline config: JamConfig): Unit = ???
9+
}
10+
private object internal extends JamCoreDsl
11+
export internal._
12+
}
13+
14+
object test {
15+
jam.brew
16+
}

tests/pos/i22593b/Main.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import scala.quoted.*
2+
3+
package jam {
4+
trait JamCoreDsl {
5+
implicit inline def defaultJamConfig: this.JamConfig =
6+
new JamConfig(brewRecRegex = ".*")
7+
class JamConfig(val brewRecRegex: String)
8+
inline def brew(implicit inline config: JamConfig): Unit = ${ brewImpl() }
9+
}
10+
private object internal extends JamCoreDsl
11+
export internal._
12+
13+
def brewImpl(using q: Quotes)(): Expr[Unit] = {
14+
findSelf
15+
'{()}
16+
}
17+
18+
private def findSelf(using q: Quotes): Unit = {
19+
import q.reflect.*
20+
def rec(s: Symbol): Option[Symbol] = s.maybeOwner match {
21+
case o if o.isNoSymbol => None
22+
case o if o.isClassDef => Option(o)
23+
case o => rec(o)
24+
}
25+
rec(Symbol.spliceOwner)
26+
}
27+
}

tests/pos/i22593b/Test.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object CoreSpec {
2+
jam.brew
3+
}

0 commit comments

Comments
 (0)