diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 65c81ce42e13..23996ce14dfb 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -4,7 +4,7 @@ package ast import core._ import Types._, Names._, NameOps._, Flags._, util.Spans._, Contexts._, Constants._ -import typer.ProtoTypes +import typer.{ ConstFold, ProtoTypes } import SymDenotations._, Symbols._, Denotations._, StdNames._, Comments._ import language.higherKinds import collection.mutable.ListBuffer @@ -408,6 +408,13 @@ object Trees { case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name)(implicit @constructorOnly src: SourceFile) extends RefTree[T] { type ThisTree[-T >: Untyped] = Select[T] + + override def denot(using Context): Denotation = typeOpt match + case ConstantType(_) if ConstFold.foldedUnops.contains(name) => + // Recover the denotation of a constant-folded selection + qualifier.typeOpt.member(name).atSignature(Signature.NotAMethod, name) + case _ => + super.denot } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature)(implicit @constructorOnly src: SourceFile) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 7eaad36b18a2..4ebe2cf41fad 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -380,6 +380,17 @@ class TreeChecker extends Phase with SymTransformer { override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) val tpe = tree.typeOpt + + // Polymorphic apply methods stay structural until Erasure + val isPolyFunctionApply = (tree.name eq nme.apply) && (tree.qualifier.typeOpt <:< defn.PolyFunctionType) + // Outer selects are pickled specially so don't require a symbol + val isOuterSelect = tree.name.is(OuterSelectName) + val isPrimitiveArrayOp = ctx.erasedTypes && nme.isPrimitiveName(tree.name) + if !(tree.isType || isPolyFunctionApply || isOuterSelect || isPrimitiveArrayOp) then + val denot = tree.denot + assert(denot.exists, i"Selection $tree with type $tpe does not have a denotation") + assert(denot.symbol.exists, i"Denotation $denot of selection $tree with type $tpe does not have a symbol") + val sym = tree.symbol val symIsFixed = tpe match { case tpe: TermRef => ctx.erasedTypes || !tpe.isMemberRef @@ -387,7 +398,7 @@ class TreeChecker extends Phase with SymTransformer { } if (sym.exists && !sym.is(Private) && !symIsFixed && - !tree.name.is(OuterSelectName)) { // outer selects have effectively fixed symbols + !isOuterSelect) { // outer selects have effectively fixed symbols val qualTpe = tree.qualifier.typeOpt val member = if (sym.is(Private)) qualTpe.member(tree.name) @@ -403,6 +414,7 @@ class TreeChecker extends Phase with SymTransformer { |qualifier type : ${tree.qualifier.typeOpt} |tree type : ${tree.typeOpt} of class ${tree.typeOpt.getClass}""") } + checkNotRepeated(super.typedSelect(tree, pt)) } diff --git a/compiler/src/dotty/tools/dotc/typer/ConstFold.scala b/compiler/src/dotty/tools/dotc/typer/ConstFold.scala index 0bf1e4a076ea..4633c187912f 100644 --- a/compiler/src/dotty/tools/dotc/typer/ConstFold.scala +++ b/compiler/src/dotty/tools/dotc/typer/ConstFold.scala @@ -23,7 +23,7 @@ object ConstFold: nme.LT, nme.GT, nme.LE, nme.GE, nme.LSL, nme.LSR, nme.ASR, nme.ADD, nme.SUB, nme.MUL, nme.DIV, nme.MOD) - private val foldedUnops = Set[Name]( + val foldedUnops = Set[Name]( nme.UNARY_!, nme.UNARY_~, nme.UNARY_+, nme.UNARY_-) def Apply[T <: Apply](tree: T)(using Context): T =