Skip to content

Commit

Permalink
Merge pull request #426 from sjrd/fix-issue-424
Browse files Browse the repository at this point in the history
Fix #424: Read and handle INLINED nodes in TypeTree position.
  • Loading branch information
sjrd authored Dec 19, 2023
2 parents e711b4a + 9d218c7 commit db863bd
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 15 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ inThisBuild(Def.settings(
Developer("sjrd", "Sébastien Doeraene", "[email protected]", url("https://github.com/sjrd/")),
Developer("bishabosha", "Jamie Thompson", "[email protected]", url("https://github.com/bishabosha")),
),
versionPolicyIntention := Compatibility.BinaryAndSourceCompatible,
versionPolicyIntention := Compatibility.BinaryCompatible,
// Ignore dependencies to internal modules whose version is like `1.2.3+4...` (see https://github.com/scalacenter/sbt-version-policy#how-to-integrate-with-sbt-dynver)
versionPolicyIgnoredInternalDependencyVersions := Some("^\\d+\\.\\d+\\.\\d+\\+\\d+".r)
))
Expand Down
7 changes: 7 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,13 @@ private[tastyquery] object Printers:
case elem: TypeMember => print(elem)
case elem: TypeTree => print(elem)
}

case InlinedTypeTree(caller, expansion) =>
for c <- caller do
print("<inlined: ")
print(c)
print(">")
print(expansion)
end print

def print(tree: WildcardTypeArgTree): Unit =
Expand Down
3 changes: 3 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Traversers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ object Traversers:
case TypeBindingsTree(bindings, body) =>
traverse(bindings)
traverse(body)
case InlinedTypeTree(caller, expansion) =>
traverse(caller)
traverse(expansion)

// TypeDefinitionTree's
case InferredTypeBoundsTree(bounds) =>
Expand Down
15 changes: 15 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,21 @@ object Trees {
override def withPos(pos: SourcePosition): TypeBindingsTree = TypeBindingsTree(bindings, body)(pos)
end TypeBindingsTree

/** A tree representing an inlined type.
*
* @param caller
* The toplevel class from which the type was inlined.
* @param expansion
* The expanded type.
*/
final case class InlinedTypeTree(caller: Option[TypeIdent | SelectTypeTree], expansion: TypeTree)(pos: SourcePosition)
extends TypeTree(pos):
override protected def calculateType: NonEmptyPrefix =
expansion.toPrefix

override final def withPos(pos: SourcePosition): InlinedTypeTree = InlinedTypeTree(caller, expansion)(pos)
end InlinedTypeTree

// --- TypeDefinitionTrees and TypeBoundsTrees ------------------------------

sealed abstract class TypeDefinitionTree(pos: SourcePosition) extends Tree(pos):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1073,20 +1073,7 @@ private[tasties] class TreeUnpickler private (
reader.readByte()
val end = reader.readEnd()
val expr = readTerm
val caller: Option[TypeIdent | SelectTypeTree] =
reader.ifBefore(end)(
tagFollowShared match {
// The caller is not specified, this is a binding (or next val or def)
case VALDEF | DEFDEF => None
case _ =>
readTypeTree match
case caller: TypeIdent => Some(caller)
case caller: SelectTypeTree => Some(caller)
case caller =>
throw TastyFormatException(s"Unexpected Inlined caller $caller $posErrorMsg")
},
None
)
val caller = readInlinedCaller(end)
val bindings = reader.until(end)(readValOrDefDef)
Inlined(expr, caller, bindings)(spn)
case SHAREDterm =>
Expand Down Expand Up @@ -1635,6 +1622,15 @@ private[tasties] class TreeUnpickler private (
val body = readTypeTree
val bindings = readStats(end).map(_.asInstanceOf[TypeMember])
TypeBindingsTree(bindings, body)(spn)
case INLINED =>
val spn = span
reader.readByte()
val end = reader.readEnd()
val expansion = readTypeTree
val caller = readInlinedCaller(end)
if reader.currentAddr != end then
throw TastyFormatException(s"Unexpected bindings in INLINED type tree $posErrorMsg")
InlinedTypeTree(caller, expansion)(spn)
case SHAREDterm =>
val spn = span
reader.readByte()
Expand All @@ -1646,6 +1642,22 @@ private[tasties] class TreeUnpickler private (
TypeWrapper(readNonEmptyPrefix())(span)
}

private def readInlinedCaller(end: Addr)(using SourceFile): Option[TypeIdent | SelectTypeTree] =
reader.ifBefore(end)(
tagFollowShared match {
// The caller is not specified, this is a binding (or next val or def)
case VALDEF | DEFDEF => None
case _ =>
readTypeTree match
case caller: TypeIdent => Some(caller)
case caller: SelectTypeTree => Some(caller)
case caller =>
throw TastyFormatException(s"Unexpected Inlined caller $caller $posErrorMsg")
},
None
)
end readInlinedCaller

private def protectReadDefiningTypeTree[A <: TypeTree](body: => A): A =
/* It is possible to find SHAREDterm's referencing REFINEDtpt, `LAMBDAtpt`, etc.
* This is bad because a these TypeTree's define symbols. If we read them
Expand Down
16 changes: 16 additions & 0 deletions tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,22 @@ class ReadTreeSuite extends RestrictedUnpicklingSuite {
assert(containsSubtree(inlined)(clue(tree)))
}

testUnpickle("inlined-path", "simple_trees.InlinedPath") { tree =>
val inlinedPath: StructureCheck = {
case SelectTypeTree(
InlinedTypeTree(
Some(TypeIdent(ObjectClassTypeName(SimpleTypeName("InlinedPath")))),
InlinedTypeTree(None, TypeWrapper(TermRefInternal(NoPrefix, xSym: Symbol)))
),
SimpleTypeName("Inner")
) if xSym.name == termName("x") =>
}
val testDef = findTree(tree) { case testDef @ DefDef(SimpleName("test"), _, _, _, _) =>
testDef
}
assert(containsSubtree(inlinedPath)(clue(testDef)))
}

testUnpickle("select-tpt", "simple_trees.SelectType") { tree =>
val selectTpt: StructureCheck = {
case ValDef(
Expand Down
21 changes: 21 additions & 0 deletions tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3677,4 +3677,25 @@ class TypeSuite extends UnrestrictedUnpicklingSuite {
case arg =>
fail("unexpected argument to @macroImpl", clues(arg))
}

testWithContext("inlined-path-issue-424") {
val InlinedPathClass = ctx.findTopLevelClass("simple_trees.InlinedPath")
val FooClass = ctx.findStaticClass("simple_trees.InlinedPath.Foo")
val InnerTypeMember = FooClass.findDecl(typeName("Inner"))

val test = InlinedPathClass.findNonOverloadedDecl(termName("test"))
val List(Left(List(x)), Left(List(inner))) = test.paramSymss: @unchecked

assert(clue(x.name) == termName("x"))
assert(clue(x).isGivenOrUsing)
assert(clue(x.declaredType).isRef(FooClass))

assert(clue(inner.name) == termName("inner"))
inner.declaredType match
case typeRef: TypeRef =>
assert(clue(typeRef.prefix).isRef(x)) // through inlining the path
assert(typeRef.isRef(InnerTypeMember))
case tpe =>
fail("unexpected type for inner", clues(tpe))
}
}
14 changes: 14 additions & 0 deletions test-sources/src/main/scala/simple_trees/InlinedPath.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package simple_trees

object InlinedPath:
trait Foo:
type Inner

transparent inline def foo(using x: Foo): x.type = x
end InlinedPath

class InlinedPath:
import InlinedPath.*

def test(using x: Foo)(inner: foo.Inner): foo.Inner = inner
end InlinedPath

0 comments on commit db863bd

Please sign in to comment.