Skip to content

Commit aa5492f

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

File tree

9 files changed

+134
-14
lines changed

9 files changed

+134
-14
lines changed

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dotty.tools
22
package dotc
33
package transform
44

5+
import ast.Trees
56
import core.Phases.*
67
import core.DenotTransformers.*
78
import core.Denotations.*
@@ -1044,7 +1045,21 @@ 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 (see tests/run/mixins.scala)
1052+
val newTraits = impl.parents.tail.filterConserve: tree =>
1053+
def isTraitConstructor = tree match
1054+
case Trees.Block(_, expr) => // Specific management for trait constructors (see tests/pos/i9213.scala)
1055+
expr.symbol.isConstructor && expr.symbol.owner.is(Flags.Trait)
1056+
case _ => tree.symbol.isConstructor && tree.symbol.owner.is(Flags.Trait)
1057+
tree.symbol.is(Flags.Trait) || isTraitConstructor
1058+
1059+
val newParents =
1060+
if impl.parents.tail eq newTraits then impl.parents
1061+
else impl.parents.head :: newTraits
1062+
cpy.TypeDef(typedTree)(rhs = cpy.Template(impl)(parents = newParents))
10481063

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

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,8 +547,24 @@ 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.isPrimitiveValueClass then
555+
val symbolParents = sym.classInfo.parents.map(_.dealias.typeSymbol)
556+
val treeParents = impl.parents.map(_.tpe.dealias.typeSymbol)
557+
assert(symbolParents == treeParents,
558+
i"""Parents of class symbol differs from the parents in the tree for $sym
559+
|
560+
|Parents in symbol: $symbolParents
561+
|Parents in tree: $treeParents
562+
|""".stripMargin)
563+
}
564+
550565
override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
551566
assert(sym.info.isInstanceOf[ClassInfo | TypeBounds], i"wrong type, expect a template or type bounds for ${sym.fullName}, but found: ${sym.info}")
567+
if sym.isClass then checkParents(tdef)
552568
super.typedTypeDef(tdef, sym)
553569
}
554570

@@ -561,6 +577,8 @@ object TreeChecker {
561577
checkOwner(impl)
562578
checkOwner(impl.constr)
563579

580+
checkParents(cdef)
581+
564582
def isNonMagicalMember(x: Symbol) =
565583
!x.isValueClassConvertMethod &&
566584
!x.name.is(DocArtifactName) &&
@@ -812,7 +830,7 @@ object TreeChecker {
812830
else err.getStackTrace.nn.mkString(" ", " \n", "")
813831

814832
report.error(
815-
s"""Malformed tree was found while expanding macro with -Xcheck-macros.
833+
em"""Malformed tree was found while expanding macro with -Xcheck-macros.
816834
|The tree does not conform to the compiler's tree invariants.
817835
|
818836
|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)