Skip to content

Commit 3345567

Browse files
committed
Add checks for the consistency of the parents in TreeChecker
1 parent 95266f2 commit 3345567

File tree

9 files changed

+139
-14
lines changed

9 files changed

+139
-14
lines changed

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import ExplicitOuter.*
3434
import core.Mode
3535
import util.Property
3636
import reporting.*
37+
import dotty.tools.dotc.ast.Trees
3738

3839
class Erasure extends Phase with DenotTransformer {
3940

@@ -1044,7 +1045,16 @@ object Erasure {
10441045

10451046
override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(using Context): Tree =
10461047
if cls.is(Flags.Erased) then erasedDef(cls)
1047-
else super.typedClassDef(cdef, cls)
1048+
else
1049+
val typedTree@TypeDef(name, impl @ Template(constr, _, self, _)) = super.typedClassDef(cdef, cls): @unchecked
1050+
// In the case where a trait extends a class, we need to strip any non trait class from the signature
1051+
// and accept the first one
1052+
val newParents = impl.parents.head :: impl.parents.tail.filter:
1053+
case Trees.Block(_, expr) =>
1054+
// Specific management for trait constructors (See #9216)
1055+
expr.symbol.isConstructor
1056+
case t => t.symbol.isConstructor || t.symbol.is(Flags.Trait)
1057+
cpy.TypeDef(typedTree)(rhs = cpy.Template(impl)(parents = newParents))
10481058

10491059
override def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree =
10501060
typed(tree.arg, pt)

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,8 +547,34 @@ object TreeChecker {
547547
i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %")
548548
}
549549

550+
private def checkParents(tree: untpd.TypeDef)(using Context): Unit = {
551+
val TypeDef(_, impl: Template) = tree: @unchecked
552+
assert(ctx.owner.isClass)
553+
val sym = ctx.owner.asClass
554+
if sym == defn.BooleanClass ||
555+
sym == defn.ByteClass ||
556+
sym == defn.CharClass ||
557+
sym == defn.DoubleClass ||
558+
sym == defn.FloatClass ||
559+
sym == defn.ShortClass ||
560+
sym == defn.IntClass ||
561+
sym == defn.LongClass ||
562+
sym == defn.UnitClass
563+
then
564+
return
565+
val symbolParents = sym.classInfo.parents.map(_.dealias.typeSymbol)
566+
val treeParents = impl.parents.map(_.tpe.dealias.typeSymbol)
567+
assert(symbolParents == treeParents,
568+
i"""Parents of class symbol differs from the parents in the tree for $sym
569+
|
570+
|Parents in symbol: $symbolParents
571+
|Parents in tree: $treeParents
572+
|""".stripMargin)
573+
}
574+
550575
override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
551576
assert(sym.info.isInstanceOf[ClassInfo | TypeBounds], i"wrong type, expect a template or type bounds for ${sym.fullName}, but found: ${sym.info}")
577+
if sym.isClass then checkParents(tdef)
552578
super.typedTypeDef(tdef, sym)
553579
}
554580

@@ -561,6 +587,8 @@ object TreeChecker {
561587
checkOwner(impl)
562588
checkOwner(impl.constr)
563589

590+
checkParents(cdef)
591+
564592
def isNonMagicalMember(x: Symbol) =
565593
!x.isValueClassConvertMethod &&
566594
!x.name.is(DocArtifactName) &&
@@ -812,7 +840,7 @@ object TreeChecker {
812840
else err.getStackTrace.nn.mkString(" ", " \n", "")
813841

814842
report.error(
815-
s"""Malformed tree was found while expanding macro with -Xcheck-macros.
843+
em"""Malformed tree was found while expanding macro with -Xcheck-macros.
816844
|The tree does not conform to the compiler's tree invariants.
817845
|
818846
|Macro was:

tests/neg-macros/i18677-a.check

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
-- Error: tests/neg-macros/i18677-a/Test_2.scala:4:6 -------------------------------------------------------------------
3+
3 |@extendFoo
4+
4 |class AFoo // error
5+
|^
6+
|Malformed tree was found while expanding macro with -Xcheck-macros.
7+
|The tree does not conform to the compiler's tree invariants.
8+
|
9+
|Macro was:
10+
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @extendFoo class AFoo()
11+
|
12+
|The macro returned:
13+
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @extendFoo class AFoo() extends Foo
14+
|
15+
|Error:
16+
|assertion failed: Parents of class symbol differs from the parents in the tree for class AFoo
17+
|
18+
|Parents in symbol: [class Object]
19+
|Parents in tree: [trait Foo]
20+
|
21+
|
22+
|stacktrace available when compiling with `-Ydebug`
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//> using -expermiental
2+
3+
import annotation.MacroAnnotation
4+
import quoted.*
5+
6+
trait Foo
7+
8+
class extendFoo extends MacroAnnotation :
9+
override def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
10+
import quotes.reflect.*
11+
tree match
12+
case ClassDef(name, ctr, p, self, body) =>
13+
val parents = List(TypeTree.of[Foo])
14+
val newTree = ClassDef.copy(tree)(name, ctr, parents, self, body)
15+
newTree :: Nil
16+
case _ =>
17+
report.error("@extendFoo can only annotate class definitions")
18+
tree :: Nil
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//> using -expermiental
2+
3+
@extendFoo
4+
class AFoo // error

tests/neg-macros/i18677-b.check

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
-- Error: tests/neg-macros/i18677-b/Test_2.scala:4:6 -------------------------------------------------------------------
3+
3 |@extendFoo
4+
4 |class AFoo // error
5+
|^
6+
|Malformed tree was found while expanding macro with -Xcheck-macros.
7+
|The tree does not conform to the compiler's tree invariants.
8+
|
9+
|Macro was:
10+
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @extendFoo class AFoo()
11+
|
12+
|The macro returned:
13+
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @extendFoo class AFoo() extends Foo
14+
|
15+
|Error:
16+
|assertion failed: Parents of class symbol differs from the parents in the tree for class AFoo
17+
|
18+
|Parents in symbol: [class Object]
19+
|Parents in tree: [class Foo]
20+
|
21+
|
22+
|stacktrace available when compiling with `-Ydebug`
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//> using -expermiental
2+
3+
import annotation.MacroAnnotation
4+
import quoted.*
5+
6+
class Foo
7+
8+
class extendFoo extends MacroAnnotation :
9+
override def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
10+
import quotes.reflect.*
11+
tree match
12+
case ClassDef(name, ctr, p, self, body) =>
13+
val parents = List(TypeTree.of[Foo])
14+
val newTree = ClassDef.copy(tree)(name, ctr, parents, self, body)
15+
newTree :: Nil
16+
case _ =>
17+
report.error("@extendFoo can only annotate class definitions")
18+
tree :: Nil
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//> using -expermiental
2+
3+
@extendFoo
4+
class AFoo // error

tests/neg-macros/wrong-owner.check

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@
55
5 |class Foo // error
66
|^
77
|Malformed tree was found while expanding macro with -Xcheck-macros.
8-
| |The tree does not conform to the compiler's tree invariants.
9-
| |
10-
| |Macro was:
11-
| |@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo()
12-
| |
13-
| |The macro returned:
14-
| |@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo() {
8+
|The tree does not conform to the compiler's tree invariants.
9+
|
10+
|Macro was:
11+
|@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo()
12+
|
13+
|The macro returned:
14+
|@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo() {
1515
| override def toString(): java.lang.String = "Hello from macro"
1616
|}
17-
| |
18-
| |Error:
19-
| |assertion failed: bad owner; method toString has owner class String, expected was class Foo
17+
|
18+
|Error:
19+
|assertion failed: bad owner; method toString has owner class String, expected was class Foo
2020
|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>
21-
| |
21+
|
2222
|stacktrace available when compiling with `-Ydebug`
23-
| |

0 commit comments

Comments
 (0)