From 03ee583d39b9fd0ae85f6d2477dc21b232cd2d06 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 28 Oct 2024 22:05:56 +0100 Subject: [PATCH] Make sure definition tree has the defined symbol 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 --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 16 ++++++++++++++++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 1 + compiler/src/dotty/tools/dotc/typer/Typer.scala | 9 ++++++--- tests/pos/i21755.scala | 11 +++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i21755.scala diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index f97baa7f7889..3777969b1076 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -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 @@ -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) => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index e62db9af520a..b401de823c6c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -999,6 +999,7 @@ class TreeUnpickler(reader: TastyReader, } } + tree.ensureHasSym(sym) tree.setDefTree } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3810bc66841e..e2a07d3832ab 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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 diff --git a/tests/pos/i21755.scala b/tests/pos/i21755.scala new file mode 100644 index 000000000000..170daed04f05 --- /dev/null +++ b/tests/pos/i21755.scala @@ -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 + } +} \ No newline at end of file