TASTy does not include reference to resolved term when using `summonAll` and `summonFrom`
Compiler version
3.3.5, 3.4.3, 3.5.2, and 3.6.3
Minimized code
https://github.com/mrdziuban/scala3-compiletime-tasty
That repo contains two files:
Test.scalawhich summons an instance ofcats.Show[String]using fourinlinemethods:val instWithSummon = summon[cats.Show[String]] val instWithSummonAll = compiletime.summonAll[cats.Show[String] *: EmptyTuple] val instWithSummonInline = compiletime.summonInline[cats.Show[String]] inline def instWithSummonFrom = compiletime.summonFrom { case s: cats.Show[String] => s }TastyQueryTest.scalawhich logs the AST of each method call usingtasty-query
Output
The output of TastyQueryTest.scala shows that the calls to instWithSummon and instWithSummonInline contain a reference to catsShowForString, which is the instance that satisfies the implicit search, but neither instWithSummonAll nor instWithSummonFrom have a reference to it
expand output
********************************************************************************
instWithSummon:
Inlined(
expr = Inlined(
expr = Ident(
name = UniqueName(underlying = SimpleName(name = "x"), separator = "$proxy", num = 1)
),
caller = None,
bindings = List()
),
caller = Some(
value = TypeIdent(name = ObjectClassTypeName(underlying = SimpleTypeName(name = "Predef")))
),
bindings = List(
ValDef(
name = UniqueName(underlying = SimpleName(name = "x"), separator = "$proxy", num = 1),
tpt = TypeWrapper(
tp = AppliedType(TypeRef(PackageRef(cats), Show), List(TypeRef(TermRef(PackageRef(scala), Predef), String)))
),
rhs = Some(value = Ident(name = SimpleName(name = "catsShowForString"))),
symbol = symbol[instWithSummon>x$proxy1]
)
)
)
********************************************************************************
********************************************************************************
instWithSummonAll:
TypeApply(
fun = Select(
qualifier = Select(
qualifier = Ident(name = SimpleName(name = "compiletime")),
name = SimpleName(name = "package$package")
),
name = SignedName(
underlying = SimpleName(name = "summonAll"),
sig = Signature(
paramsSig = List(TypeLen(len = 1)),
resSig = SignatureName(
items = List(SimpleName(name = "scala"), SimpleName(name = "Product"))
)
),
target = SimpleName(name = "summonAll")
)
),
args = List(
AppliedTypeTree(
tycon = TypeIdent(name = SimpleTypeName(name = "*:")),
args = List(
AppliedTypeTree(
tycon = SelectTypeTree(
qualifier = TypeWrapper(tp = PackageRef(cats)),
name = SimpleTypeName(name = "Show")
),
args = List(TypeIdent(name = SimpleTypeName(name = "String")))
),
TypeIdent(name = SimpleTypeName(name = "EmptyTuple"))
)
)
)
)
********************************************************************************
********************************************************************************
instWithSummonInline:
Ident(name = SimpleName(name = "catsShowForString"))
********************************************************************************
********************************************************************************
instWithSummonFrom:
Typed(
expr = InlineMatch(
selector = None,
cases = List(
CaseDef(
pattern = Bind(
name = SimpleName(name = "s"),
body = TypeTest(
body = WildcardPattern(
tpe = AppliedType(TypeRef(PackageRef(cats), Show), List(TypeRef(TermRef(PackageRef(scala), Predef), String)))
),
tpt = AppliedTypeTree(
tycon = SelectTypeTree(
qualifier = TypeWrapper(tp = PackageRef(cats)),
name = SimpleTypeName(name = "Show")
),
args = List(TypeIdent(name = SimpleTypeName(name = "String")))
)
),
symbol = symbol[instWithSummonFrom>s]
),
guard = None,
body = Block(
stats = List(),
expr = Typed(
expr = Ident(name = SimpleName(name = "s")),
tpt = TypeWrapper(
tp = AppliedType(TypeRef(PackageRef(cats), Show), List(TypeRef(TermRef(PackageRef(scala), Predef), String)))
)
)
)
)
)
),
tpt = TypeWrapper(
tp = AppliedType(TypeRef(PackageRef(cats), Show), List(TypeRef(TermRef(PackageRef(scala), Predef), String)))
)
)
********************************************************************************
Expectation
I would expect the TASTy for all four of these calls to have a reference to catsShowForString.
I've started debugging this focused first on summonAll as it compares to summonInline.
I've found that summonInline is inlined during the typer phase (and therefore before the pickler phase) because it's transparent and Inlines.needsInlining returns true during the typer phase for transparent symbols.
summonAll is not inlined until the inlining phase (after the pickler phase) because it's not transparent.
I can confirm that if I make it transparent then the tree the pickler phase sees has a reference to the resolved term. To do so, I updated TreePickler#pickleTree to print every tree it receives with tree.show and used this code:
val ordInt = compiletime.summonAll[Ordering[Int] *: EmptyTuple]
As-is (without transparent) I get:
val ordInt: Ordering[Int] *: EmptyTuple =
compiletime.package$package.summonAll[*:[Ordering[Int], EmptyTuple]]
After adding transparent I get:
val ordInt: Ordering[Int] *: EmptyTuple =
Tuple1.apply[scala.math.Ordering.Int.type](scala.math.Ordering.Int):
Ordering[Int] *: EmptyTuple
I see that summonInline was made transparent in https://github.com/scala/scala3/commit/254879888d4813d7cc0d336702dd2c824a3956fa and that summonAll wasn't added until until a couple months later in https://github.com/scala/scala3/commit/3aa00b19107b6383bf059f8c6108e5af641790f3. @nicolasstucki do you have any thoughts on whether it would be reasonable to change summonAll to be transparent?
Continuing to debug with summonFrom.
I've found that summonFrom is converted to an InlineMatch during the typer phase in Applications.scala, which results in two checks in Inlines.needsInlining to be false -- isInlineable(tree.symbol) and needsTransparentInlining(tree).
The end result is the same -- the InlineMatch is not inlined until the inlining phase, after the pickler. I'm much less clear on how to address this, especially given the comment in Applications.scala about how summonFrom needs to expand lazily.
related: https://github.com/scala/scala3/issues/16313
to be watertight we should really abort compilation if any post-pickling implicit search is attempted from a non-transparent inline method - transitively
@mrdziuban thanks for the great issue summary!
This issue was proposed for a Spree. However I am afraid that it might require more discussions and specifications before we can implement something.
@bishabosha @sjrd @nicolasstucki what is the current status of summon* methods? Do you know if and how we could move forward with this issue?
The fact that INLINED tasty nodes dont store a reference where they were inlined from, is that a feature or an oversight?