Skip to content

Commit

Permalink
Add checks for the consistency of the parents in TreeChecker
Browse files Browse the repository at this point in the history
  • Loading branch information
hamzaremmal committed Feb 12, 2024
1 parent 95266f2 commit aa5492f
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 14 deletions.
17 changes: 16 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools
package dotc
package transform

import ast.Trees
import core.Phases.*
import core.DenotTransformers.*
import core.Denotations.*
Expand Down Expand Up @@ -1044,7 +1045,21 @@ object Erasure {

override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(using Context): Tree =
if cls.is(Flags.Erased) then erasedDef(cls)
else super.typedClassDef(cdef, cls)
else
val typedTree@TypeDef(name, impl @ Template(constr, _, self, _)) = super.typedClassDef(cdef, cls): @unchecked
// In the case where a trait extends a class, we need to strip any non trait class from the signature
// and accept the first one (see tests/run/mixins.scala)
val newTraits = impl.parents.tail.filterConserve: tree =>
def isTraitConstructor = tree match
case Trees.Block(_, expr) => // Specific management for trait constructors (see tests/pos/i9213.scala)
expr.symbol.isConstructor && expr.symbol.owner.is(Flags.Trait)
case _ => tree.symbol.isConstructor && tree.symbol.owner.is(Flags.Trait)
tree.symbol.is(Flags.Trait) || isTraitConstructor

val newParents =
if impl.parents.tail eq newTraits then impl.parents
else impl.parents.head :: newTraits
cpy.TypeDef(typedTree)(rhs = cpy.Template(impl)(parents = newParents))

override def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree =
typed(tree.arg, pt)
Expand Down
20 changes: 19 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,24 @@ object TreeChecker {
i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %")
}

private def checkParents(tree: untpd.TypeDef)(using Context): Unit = {
val TypeDef(_, impl: Template) = tree: @unchecked
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)
}

override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
assert(sym.info.isInstanceOf[ClassInfo | TypeBounds], i"wrong type, expect a template or type bounds for ${sym.fullName}, but found: ${sym.info}")
if sym.isClass then checkParents(tdef)
super.typedTypeDef(tdef, sym)
}

Expand All @@ -561,6 +577,8 @@ object TreeChecker {
checkOwner(impl)
checkOwner(impl.constr)

checkParents(cdef)

def isNonMagicalMember(x: Symbol) =
!x.isValueClassConvertMethod &&
!x.name.is(DocArtifactName) &&
Expand Down Expand Up @@ -812,7 +830,7 @@ object TreeChecker {
else err.getStackTrace.nn.mkString(" ", " \n", "")

report.error(
s"""Malformed tree was found while expanding macro with -Xcheck-macros.
em"""Malformed tree was found while expanding macro with -Xcheck-macros.
|The tree does not conform to the compiler's tree invariants.
|
|Macro was:
Expand Down
22 changes: 22 additions & 0 deletions tests/neg-macros/i18677-a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

-- Error: tests/neg-macros/i18677-a/Test_2.scala:4:6 -------------------------------------------------------------------
3 |@extendFoo
4 |class AFoo // 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.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @extendFoo class AFoo()
|
|The macro returned:
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @extendFoo class AFoo() extends Foo
|
|Error:
|assertion failed: Parents of class symbol differs from the parents in the tree for class AFoo
|
|Parents in symbol: [class Object]
|Parents in tree: [trait Foo]
|
|
|stacktrace available when compiling with `-Ydebug`
18 changes: 18 additions & 0 deletions tests/neg-macros/i18677-a/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//> using -expermiental

import annotation.MacroAnnotation
import quoted.*

trait Foo

class extendFoo extends MacroAnnotation :
override def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect.*
tree match
case ClassDef(name, ctr, p, self, body) =>
val parents = List(TypeTree.of[Foo])
val newTree = ClassDef.copy(tree)(name, ctr, parents, self, body)
newTree :: Nil
case _ =>
report.error("@extendFoo can only annotate class definitions")
tree :: Nil
4 changes: 4 additions & 0 deletions tests/neg-macros/i18677-a/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//> using -expermiental

@extendFoo
class AFoo // error
22 changes: 22 additions & 0 deletions tests/neg-macros/i18677-b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

-- Error: tests/neg-macros/i18677-b/Test_2.scala:4:6 -------------------------------------------------------------------
3 |@extendFoo
4 |class AFoo // 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.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @extendFoo class AFoo()
|
|The macro returned:
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @extendFoo class AFoo() extends Foo
|
|Error:
|assertion failed: Parents of class symbol differs from the parents in the tree for class AFoo
|
|Parents in symbol: [class Object]
|Parents in tree: [class Foo]
|
|
|stacktrace available when compiling with `-Ydebug`
18 changes: 18 additions & 0 deletions tests/neg-macros/i18677-b/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//> using -expermiental

import annotation.MacroAnnotation
import quoted.*

class Foo

class extendFoo extends MacroAnnotation :
override def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect.*
tree match
case ClassDef(name, ctr, p, self, body) =>
val parents = List(TypeTree.of[Foo])
val newTree = ClassDef.copy(tree)(name, ctr, parents, self, body)
newTree :: Nil
case _ =>
report.error("@extendFoo can only annotate class definitions")
tree :: Nil
4 changes: 4 additions & 0 deletions tests/neg-macros/i18677-b/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//> using -expermiental

@extendFoo
class AFoo // error
23 changes: 11 additions & 12 deletions tests/neg-macros/wrong-owner.check
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@
5 |class Foo // 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.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo()
| |
| |The macro returned:
| |@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo() {
|The tree does not conform to the compiler's tree invariants.
|
|Macro was:
|@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo()
|
|The macro returned:
|@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo() {
| override def toString(): java.lang.String = "Hello from macro"
|}
| |
| |Error:
| |assertion failed: bad owner; method toString has owner class String, expected was class Foo
|
|Error:
|assertion failed: bad owner; method toString has owner class String, expected was class Foo
|owner chain = method toString, class String, package java.lang, package java, package <root>, ctxOwners = class Foo, class Foo, package <empty>, package <empty>, package <empty>, package <root>, package <root>, package <root>, package <root>, package <root>, package <root>, <none>, <none>, <none>, <none>, <none>
| |
|
|stacktrace available when compiling with `-Ydebug`
| |

0 comments on commit aa5492f

Please sign in to comment.