Skip to content

Commit b3baab2

Browse files
committed
Don't recurse into type arguments in inliner map
Inlined type arguments should be left alone in the inliner TreeTypeMap, analogous to inlined term arguments.
1 parent 8ab9133 commit b3baab2

File tree

5 files changed

+59
-11
lines changed

5 files changed

+59
-11
lines changed

compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import core.tasty.TreePickler.Hole
2222
* @param newOwners New owners, replacing previous owners.
2323
* @param substFrom The symbols that need to be substituted.
2424
* @param substTo The substitution targets.
25+
* @param stopAtInlinedArgument Whether one should stop at an inlined argument,
26+
* i.e. a node of form Inlined(EmptyTree, _, _).
2527
*
2628
* The reason the substitution is broken out from the rest of the type map is
2729
* that all symbols have to be substituted at the same time. If we do not do this,
@@ -31,14 +33,19 @@ import core.tasty.TreePickler.Hole
3133
* do S2 we get outer#2.inner#4. But that means that the named type outer#2.inner
3234
* gets two different denotations in the same period. Hence, if -Yno-double-bindings is
3335
* set, we would get a data race assertion error.
36+
*
37+
* Note: TreeTypeMap is final, since derived maps are created dynamically for
38+
* nested scopes. Any subclass of TreeTypeMap would revert to the standard
39+
* TreeTypeMap in these recursive invocations.
3440
*/
35-
class TreeTypeMap(
41+
final class TreeTypeMap(
3642
val typeMap: Type => Type = IdentityTypeMap,
3743
val treeMap: tpd.Tree => tpd.Tree = identity _,
3844
val oldOwners: List[Symbol] = Nil,
3945
val newOwners: List[Symbol] = Nil,
4046
val substFrom: List[Symbol] = Nil,
41-
val substTo: List[Symbol] = Nil)(using Context) extends tpd.TreeMap {
47+
val substTo: List[Symbol] = Nil,
48+
stopAtInlinedArgument: Boolean = false)(using Context) extends tpd.TreeMap {
4249
import tpd._
4350

4451
/** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */
@@ -76,8 +83,11 @@ class TreeTypeMap(
7683
updateDecls(prevStats.tail, newStats.tail)
7784
}
7885

79-
/** If true, stop return Inlined(Empty, _, _) nodes unchanged */
80-
def stopAtInlinedArgument: Boolean = false
86+
def transformInlined(tree: tpd.Inlined)(using Context): tpd.Tree =
87+
val Inlined(call, bindings, expanded) = tree
88+
val (tmap1, bindings1) = transformDefs(bindings)
89+
val expanded1 = tmap1.transform(expanded)
90+
cpy.Inlined(tree)(call, bindings1, expanded1)
8191

8292
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = treeMap(tree) match {
8393
case impl @ Template(constr, parents, self, _) =>
@@ -111,7 +121,9 @@ class TreeTypeMap(
111121
cpy.Block(blk)(stats1, expr1)
112122
case inlined @ Inlined(call, bindings, expanded) =>
113123
if stopAtInlinedArgument && call.isEmpty then
114-
inlined
124+
expanded match
125+
case expanded: TypeTree => assert(bindings.isEmpty); expanded
126+
case _ => inlined
115127
else
116128
val (tmap1, bindings1) = transformDefs(bindings)
117129
val expanded1 = tmap1.transform(expanded)
@@ -177,7 +189,8 @@ class TreeTypeMap(
177189
from ++ oldOwners,
178190
to ++ newOwners,
179191
from ++ substFrom,
180-
to ++ substTo)
192+
to ++ substTo,
193+
stopAtInlinedArgument)
181194
}
182195

183196
/** Apply `typeMap` and `ownerMap` to given symbols `syms`

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -746,16 +746,15 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
746746
val inlinedSingleton = singleton(t).withSpan(argSpan)
747747
inlinedFromOutside(inlinedSingleton)(tree.span)
748748
case Some(t) if tree.isType =>
749-
TypeTree(t).withSpan(argSpan)
749+
inlinedFromOutside(TypeTree(t).withSpan(argSpan))(tree.span)
750750
case _ => tree
751751
}
752752
case tree => tree
753753
},
754754
oldOwners = inlinedMethod :: Nil,
755-
newOwners = ctx.owner :: Nil
756-
)(using inlineCtx) {
757-
override def stopAtInlinedArgument: Boolean = true
758-
}
755+
newOwners = ctx.owner :: Nil,
756+
stopAtInlinedArgument = true
757+
)(using inlineCtx)
759758

760759
// Apply inliner to `rhsToInline`, split off any implicit bindings from result, and
761760
// make them part of `bindingsBuf`. The expansion is then the tree that remains.

tests/pos/i11350.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//case class A[T](action: A[T] ?=> String) // now disallowed
2+
3+
class A1[T](action: A1[T] ?=> String = (_: A1[T]) ?=> "") // works
4+
//case class A2[T](action: A2[?] ?=> String) // now disallowed
5+
//case class A3[T](action: A3[T] => String) // now disallowed

tests/pos/i11894.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class CallbackTo[A](val x: A):
2+
inline def runNow(): A = x
3+
inline def toScalaFn: () => A = () => runNow()
4+
def toOption(): Option[A] =
5+
val y: CallbackTo[Option[A]] = ???
6+
val f: () => Option[A] = y.toScalaFn // error
7+
f()

tests/pos/i11894b.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
object CallbackTo {
2+
extension [A](self: CallbackTo[Option[A]]) {
3+
inline def asOption: Option[A] =
4+
self.toScalaFn()
5+
}
6+
}
7+
8+
final class CallbackTo[A] (val x: List[A]) {
9+
10+
inline def runNow(): A =
11+
x.head
12+
13+
inline def toScalaFn: () => A =
14+
() => runNow()
15+
16+
def map[B](f: A => B): CallbackTo[B] =
17+
???
18+
19+
def toOption: Option[A] = {
20+
val x = map[Option[A]](Some(_))
21+
val y = x: CallbackTo[Option[A]] // ok: type is what we expect
22+
y.asOption // error
23+
}
24+
}

0 commit comments

Comments
 (0)