Skip to content

Commit a0ea484

Browse files
authored
Add checks for the consistency of the parents in TreeChecker (#18935)
This PR adds a check for the parents in the TreeChecker. In the context of macro annotations, this PR doesn't allow the modification of the parents. This restriction will probably be partially lifted as we come up with the final specification. This PR is related to #18677 put does not close it.
2 parents 6efcdba + aa5492f commit a0ea484

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)