Skip to content

Fix synthetic type bounds in Tasty reflect #4601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ object TastyImpl extends scala.tasty.Tasty {

object Synthetic extends SyntheticExtractor {
def unapply(x: TypeTree)(implicit ctx: Context): Boolean = x match {
case Trees.TypeTree() => true
case x @ Trees.TypeTree() => !x.tpe.isInstanceOf[Types.TypeBounds]
case _ => false
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the name synthetic, I cannot justify the change. Maybe a doc for Synthetic, I naively assume synthetic bounds are also synthetic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add some documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Expand Down Expand Up @@ -610,10 +610,10 @@ object TastyImpl extends scala.tasty.Tasty {

// ----- TypeBoundsTrees ------------------------------------------------

type TypeBoundsTree = tpd.TypeBoundsTree
type TypeBoundsTree = tpd.Tree

def TypeBoundsTreeDeco(x: TypeBoundsTree): TypeBoundsTreeAPI = new TypeBoundsTreeAPI {
def tpe(implicit ctx: Context): TypeBounds = x.tpe.bounds
def tpe(implicit ctx: Context): TypeBounds = x.tpe.asInstanceOf[Types.TypeBounds]
}

def typeBoundsTreeClassTag: ClassTag[TypeBoundsTree] = implicitly[ClassTag[TypeBoundsTree]]
Expand All @@ -625,6 +625,13 @@ object TastyImpl extends scala.tasty.Tasty {
}
}

object SyntheticBounds extends SyntheticBoundsExtractor {
def unapply(x: TypeBoundsTree)(implicit ctx: Context): Boolean = x match {
case x @ Trees.TypeTree() => x.tpe.isInstanceOf[Types.TypeBounds]
case _ => false
}
}

// ===== Types ====================================================

type TypeOrBounds = Types.Type
Expand Down
9 changes: 9 additions & 0 deletions library/src/scala/tasty/Tasty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,10 @@ abstract class Tasty { tasty =>

def unapply(x: TypeTree)(implicit ctx: Context): Boolean

/** TypeTree containing an inferred type */
val Synthetic: SyntheticExtractor
abstract class SyntheticExtractor {
/** Matches a TypeTree containing an inferred type */
def unapply(x: TypeTree)(implicit ctx: Context): Boolean
}

Expand Down Expand Up @@ -456,6 +458,13 @@ abstract class Tasty { tasty =>
def unapply(x: TypeBoundsTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)]
}

/** TypeBoundsTree containing an inferred type bounds */
val SyntheticBounds: SyntheticBoundsExtractor
abstract class SyntheticBoundsExtractor {
/** Matches a TypeBoundsTree containing inferred type bounds */
def unapply(x: TypeBoundsTree)(implicit ctx: Context): Boolean
}

// ===== Types ====================================================

type TypeOrBounds
Expand Down
5 changes: 4 additions & 1 deletion library/src/scala/tasty/util/ShowExtractors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class ShowExtractors[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this += "TypeTree.Annotated(" += arg += ", " += annot += ")"
case TypeBoundsTree(lo, hi) =>
this += "TypeBoundsTree(" += lo += ", " += hi += ")"
case SyntheticBounds() =>
this += s"SyntheticBounds()"
}

def visitCaseDef(x: CaseDef): Buffer = {
Expand Down Expand Up @@ -188,7 +190,8 @@ class ShowExtractors[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
case Type.PolyType(argNames, argBounds, resType) =>
this += "Type.PolyType(" ++= argNames += ", " ++= argBounds += ", " += resType += ")"
case Type.TypeLambda(argNames, argBounds, resType) =>
this += "Type.TypeLambda(" ++= argNames += ", " ++= argBounds += ", " += resType += ")"
// resType is not printed to avoid cycles
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious what kind of cycles can happen here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For definitions, we use the tree of the definition. Somewhere in the body of the type lambda there was a reference to itself and then tried to reprint itself in a loop.

this += "Type.TypeLambda(" ++= argNames += ", " ++= argBounds += ", _)"
case TypeBounds(lo, hi) =>
this += "TypeBounds(" += lo += ", " += hi += ")"
case NoPrefix() =>
Expand Down
16 changes: 11 additions & 5 deletions library/src/scala/tasty/util/ShowSourceCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,11 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this += " <: "
printTypeTree(hi)
}
case tpt@TypeTree() =>
case rhs @ SyntheticBounds() =>
printTypeOrBound(rhs.tpe)
case rhs @ TypeTree() =>
this += " = "
printTypeTree(tpt)
printTypeTree(rhs)
}
}

Expand Down Expand Up @@ -604,13 +606,19 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
printTypeTree(lo)
this += " <: "
printTypeTree(hi)
case tpt @ SyntheticBounds() =>
printTypeOrBound(tpt.tpe)
case tpt @ TypeTree() =>
printTypeTree(tpt)
}

def printTypeTree(tree: TypeTree): Buffer = tree match {
case TypeTree.Synthetic() =>
printType(tree.tpe)
tree.tpe match {
case tpe @ Type.TypeRef(name, _) if name.endsWith("$") => this += ".type"
case tpe => this
}

case TypeTree.TypeIdent(name) =>
printType(tree.tpe)
Expand Down Expand Up @@ -709,9 +717,7 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
case Type.TypeRef(name, prefix) =>
prefix match {
case NoPrefix() | Type.ThisType(Types.EmptyPackage()) =>
case prefix@Type() =>
printType(prefix)
this += "."
case prefix@Type() => printType(prefix) += "."
}
this += name.stripSuffix("$")

Expand Down
43 changes: 43 additions & 0 deletions tests/pos/i2104b.decompiled
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/** Decompiled from out/posTestFromTasty/pos/i2104b/Cons.class */
class Cons[H, T]() extends java.lang.Object
object Cons {
def apply[H, T](h: H, t: T): Cons[H, T] = scala.Predef.???
def unapply[H, T](t: Cons[H, T]): scala.Option[Pair[H, T]] = scala.Predef.???
}
/** Decompiled from out/posTestFromTasty/pos/i2104b/Pair.class */
case class Pair[A, B](_1: A, _2: B) {
override def hashCode(): scala.Int = {
var acc: scala.Int = 2479866
acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(Pair.this._1))
acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(Pair.this._2))
scala.runtime.Statics.finalizeHash(acc, 2)
}
override def equals(x$0: scala.Any): scala.Boolean = this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match {
case x$0: Pair[Pair.this.A, Pair.this.B] =>
this._1.==(x$0._1).&&(this._2.==(x$0._2))
case _ =>
false
})
override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(this)
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[Pair[Pair.this.A, Pair.this.B]]
override def productArity: scala.Int = 2
override def productPrefix: java.lang.String = "Pair"
override def productElement(n: scala.Int): scala.Any = n match {
case 0 =>
this._1
case 1 =>
this._2
case _ =>
throw new java.lang.IndexOutOfBoundsException(n.toString())
}
}
object Pair extends scala.AnyRef
/** Decompiled from out/posTestFromTasty/pos/i2104b/Test.class */
object Test {
def main(args: scala.Array[scala.Predef.String]): scala.Unit = {
Cons.apply[scala.Option[scala.Int], scala.None.type](scala.Option.apply[scala.Int](1), scala.None) match {
case Cons(scala.Some(i), scala.None) =>
()
}
}
}
16 changes: 16 additions & 0 deletions tests/pos/i2104b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
case class Pair[A, B](_1: A, _2: B)

trait Cons[+H, +T]

object Cons {
def apply[H, T](h: H, t: T): Cons[H, T] = ???
def unapply[H, T](t: Cons[H, T]): Option[Pair[H, T]] = ???
}

object Test {
def main(args: Array[String]): Unit = {
Cons(Option(1), None) match {
case Cons(Some(i), None) =>
}
}
}
7 changes: 6 additions & 1 deletion tests/pos/tasty/definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,16 @@ object definitions {
case ByName(tpt: TypeTree)
}

/** Trees denoting type bounds*/
/** Trees denoting type bounds */
case class TypeBoundsTree(loBound: TypeTree, hiBound: TypeTree) extends Tree {
def tpe: Type.TypeBounds = ???
}

/** Trees denoting type infered bounds */
case class SyntheticBounds() extends Tree {
def tpe: Type.TypeBounds = ???
}

/** Trees denoting patterns */
enum Pattern extends Positioned {
def tpe: Type = ???
Expand Down
6 changes: 3 additions & 3 deletions tests/run/tasty-extractors-owners.check
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ baz2
ValDef("foo2", TypeTree.Synthetic(), None)

<init>
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Synthetic()), DefDef("b", Nil, Nil, TypeTree.Synthetic(), None), ValDef("b2", TypeTree.Synthetic(), None)))
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", SyntheticBounds()), DefDef("b", Nil, Nil, TypeTree.Synthetic(), None), ValDef("b2", TypeTree.Synthetic(), None)))

b
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Synthetic()), DefDef("b", Nil, Nil, TypeTree.Synthetic(), None), ValDef("b2", TypeTree.Synthetic(), None)))
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", SyntheticBounds()), DefDef("b", Nil, Nil, TypeTree.Synthetic(), None), ValDef("b2", TypeTree.Synthetic(), None)))

b2
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Synthetic()), DefDef("b", Nil, Nil, TypeTree.Synthetic(), None), ValDef("b2", TypeTree.Synthetic(), None)))
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", SyntheticBounds()), DefDef("b", Nil, Nil, TypeTree.Synthetic(), None), ValDef("b2", TypeTree.Synthetic(), None)))