Skip to content

Commit

Permalink
Add check for parents in Quotes (#i19842)
Browse files Browse the repository at this point in the history
  • Loading branch information
hamzaremmal committed Mar 12, 2024
1 parent 3694d95 commit 1efe9ef
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 27 deletions.
20 changes: 12 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ object TreeChecker {
}
}.apply(tp0)

def checkParents(sym: ClassSymbol, parents: List[tpd.Tree])(using Context): Unit =
val symbolParents = sym.classInfo.parents.map(_.dealias.typeSymbol)
val treeParents = parents.map(_.tpe.dealias.typeSymbol)
assert(symbolParents == treeParents,
i"""Parents of class symbol differs from the parents in the tree for $sym
|
|Parents in symbol: $symbolParents
|Parents in tree: $treeParents
|""".stripMargin)
end checkParents

/** Run some additional checks on the nodes of the trees. Specifically:
*
* - TypeTree can only appear in TypeApply args, New, Typed tpt, Closure
Expand Down Expand Up @@ -570,14 +581,7 @@ object TreeChecker {
assert(ctx.owner.isClass)
val sym = ctx.owner.asClass
if !sym.isPrimitiveValueClass then
val symbolParents = sym.classInfo.parents.map(_.dealias.typeSymbol)
val treeParents = impl.parents.map(_.tpe.dealias.typeSymbol)
assert(symbolParents == treeParents,
i"""Parents of class symbol differs from the parents in the tree for $sym
|
|Parents in symbol: $symbolParents
|Parents in tree: $treeParents
|""".stripMargin)
TreeChecker.checkParents(sym, impl.parents)
}

override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import scala.quoted.runtime.impl.printers.*

import scala.reflect.TypeTest
import dotty.tools.dotc.core.NameKinds.ExceptionBinderName
import dotty.tools.dotc.transform.TreeChecker

object QuotesImpl {

Expand Down Expand Up @@ -253,6 +254,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
(cdef.name.toString, cdef.constructor, cdef.parents, cdef.self, rhs.body)

def module(module: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): (ValDef, ClassDef) = {
if xCheckMacro then TreeChecker.checkParents(module.moduleClass.asClass, parents)
val cls = module.moduleClass
val clsDef = ClassDef(cls, parents, body)
val newCls = Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil)
Expand Down
26 changes: 8 additions & 18 deletions tests/neg-macros/i19842.check → tests/neg-macros/i19842-a.check
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
-- Error: tests/neg-macros/i19842/Test.scala:9:50 ----------------------------------------------------------------------
-- Error: tests/neg-macros/i19842-a/Test.scala:9:50 --------------------------------------------------------------------
9 |@main def Test = summon[Serializer[ValidationCls]] // error
| ^
|Malformed tree was found while expanding macro with -Xcheck-macros.
|The tree does not conform to the compiler's tree invariants.
|
|Macro was:
|scala.quoted.runtime.Expr.splice[Serializer[ValidationCls]](((contextual$2: scala.quoted.Quotes) ?=> Macros.makeSerializer[ValidationCls](scala.quoted.Type.of[ValidationCls](contextual$2), contextual$2)))
|
|The macro returned:
|{
| object objectSerializer$macro$1 extends Serializer[ValidationCls] { this: objectSerializer$macro$1.type =>
|
| }
| objectSerializer$macro$1
|}
|
|Error:
|assertion failed: Parents of class symbol differs from the parents in the tree for object objectSerializer$macro$1
|Exception occurred while executing macro expansion.
|java.lang.AssertionError: assertion failed: Parents of class symbol differs from the parents in the tree for object objectSerializer$macro$1
|
|Parents in symbol: [class Object, trait Serializer]
|Parents in tree: [trait Serializer]
|
| at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:209)
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:257)
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:256)
| at Macros$.makeSerializer(Macro.scala:25)
|
|stacktrace available when compiling with `-Ydebug`
|---------------------------------------------------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ object Macros {
name,
Flags.Implicit,
Flags.EmptyFlags,
// Without TypeRep.of[Object] it would fail with java.lang.AssertionError: assertion failed: First parent must be a class
List(TypeRepr.of[Object], TypeRepr.of[Serializer[T]]),
_ => Nil,
Symbol.noSymbol
Expand Down
File renamed without changes.
22 changes: 22 additions & 0 deletions tests/neg-macros/i19842-b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Error: tests/neg-macros/i19842-b/Test.scala:9:50 --------------------------------------------------------------------
9 |@main def Test = summon[Serializer[ValidationCls]] // error
| ^
|Exception occurred while executing macro expansion.
|java.lang.AssertionError: assertion failed: Parents of class symbol differs from the parents in the tree for object objectSerializer$macro$1
|
|Parents in symbol: [class Object, trait Serializer]
|Parents in tree: [class Object, trait Serializer, trait Foo]
|
| at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:209)
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:257)
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:256)
| at Macros$.makeSerializer(Macro.scala:27)
|
|---------------------------------------------------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from Test.scala:5
5 | implicit inline def implicitMakeSerializer[T]: Serializer[T] = ${ Macros.makeSerializer[T] }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
---------------------------------------------------------------------------------------------------------------------
31 changes: 31 additions & 0 deletions tests/neg-macros/i19842-b/Macro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//> using options -experimental -Yno-experimental

import scala.annotation.{experimental, targetName}
import scala.quoted.*
import scala.util.Try

trait Foo

object Macros {
def makeSerializer[T: Type](using Quotes): Expr[Serializer[T]] = {
import quotes.reflect.*

val tpe: TypeRepr = TypeRepr.of[T]
val name: String = Symbol.freshName("objectSerializer")

val modSym: Symbol = Symbol.newModule(
Symbol.spliceOwner,
name,
Flags.Implicit,
Flags.EmptyFlags,
List(TypeRepr.of[Object], TypeRepr.of[Serializer[T]]),
_ => Nil,
Symbol.noSymbol
)

val (modValDef: ValDef, modClassDef: ClassDef) =
ClassDef.module(modSym, List(TypeTree.of[Object], TypeTree.of[Serializer[T]], TypeTree.of[Foo]), Nil)

Block(List(modValDef, modClassDef), Ref(modSym)).asExprOf[Serializer[T]]
}
}
9 changes: 9 additions & 0 deletions tests/neg-macros/i19842-b/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

trait Serializer[@specialized T]

object Serializer:
implicit inline def implicitMakeSerializer[T]: Serializer[T] = ${ Macros.makeSerializer[T] }

case class ValidationCls(string: String)

@main def Test = summon[Serializer[ValidationCls]] // error

0 comments on commit 1efe9ef

Please sign in to comment.