Skip to content

Commit

Permalink
Make sure definition tree has the defined symbol (#21851)
Browse files Browse the repository at this point in the history
It turns out it could have the wrong symbol referring to a same-named
definition in the superclass under some recursive definition of a self
type. This caused a crash in pickler in #21755 because we now have two
different definitions in two different classes that have the same
symbol.

Fixes #21755
  • Loading branch information
odersky authored Nov 8, 2024
2 parents caac72a + 03ee583 commit 9e266e4
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 3 deletions.
16 changes: 16 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Symbols.*, StdNames.*, Annotations.*, Trees.*, Symbols.*
import Decorators.*, DenotTransformers.*
import collection.{immutable, mutable}
import util.{Property, SourceFile}
import config.Printers.typr
import NameKinds.{TempResultName, OuterSelectName}
import typer.ConstFold

Expand Down Expand Up @@ -1165,6 +1166,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
tree
}

/** Make sure tree has given symbol. This is called when typing or unpickling
* a ValDef or DefDef. It turns out that under very rare circumstances the symbol
* computed for a tree is not correct. The only known test case is i21755.scala.
* Here we have a self type that mentions a supertype as well as a type parameter
* upper-bounded by the current class and it turns out that we compute the symbol
* for a member method (named `root` in this case) in a subclass to be the
* corresponding symbol in the superclass. It is not known what are the precise
* conditions where this happens, but my guess would be that it's connected to the
* recursion in the self type.
*/
def ensureHasSym(sym: Symbol)(using Context): Unit =
if sym.exists && sym != tree.symbol then
typr.println(i"correcting definition symbol from ${tree.symbol.showLocated} to ${sym.showLocated}")
tree.overwriteType(NamedType(sym.owner.thisType, sym.asTerm.name, sym.denot))

def etaExpandCFT(using Context): Tree =
def expand(target: Tree, tp: Type)(using Context): Tree = tp match
case defn.ContextFunctionType(argTypes, resType) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,7 @@ class TreeUnpickler(reader: TastyReader,
}
}

tree.ensureHasSym(sym)
tree.setDefTree
}

Expand Down
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2949,19 +2949,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val ddef2 = assignType(cpy.DefDef(ddef)(name, paramss1, tpt1, rhs1), sym)

postProcessInfo(ddef2, sym)
ddef2.setDefTree
//todo: make sure dependent method types do not depend on implicits or by-name params
//todo: make sure dependent method types do not depend on implicits or by-name params
}

/** (1) Check that the signature of the class member does not return a repeated parameter type
* (2) If info is an erased class, set erased flag of member
* (3) Check that erased classes are not parameters of polymorphic functions.
* (4) Make sure the definition's symbol is `sym`.
* (5) Set the `defTree` of `sym` to be `mdef`.
*/
private def postProcessInfo(mdef: MemberDef, sym: Symbol)(using Context): Unit =
private def postProcessInfo(mdef: MemberDef, sym: Symbol)(using Context): MemberDef =
if (!sym.isOneOf(Synthetic | InlineProxy | Param) && sym.info.finalResultType.isRepeatedParam)
report.error(em"Cannot return repeated parameter type ${sym.info.finalResultType}", sym.srcPos)
if !sym.is(Module) && !sym.isConstructor && sym.info.finalResultType.isErasedClass then
sym.setFlag(Erased)
mdef.ensureHasSym(sym)
mdef.setDefTree

def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = ctx.profiler.onTypedDef(sym) {
val TypeDef(name, rhs) = tdef
Expand Down
11 changes: 11 additions & 0 deletions tests/pos/i21755.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
trait GraphTraversal {
type NodeT

protected trait Properties {
def root: NodeT
}

abstract protected class TraverserMethods[A, +CC <: TraverserMethods[A, CC]] { this: CC with Properties =>
def root: NodeT
}
}

0 comments on commit 9e266e4

Please sign in to comment.