diff options
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 48 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 10 |
2 files changed, 50 insertions, 8 deletions
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 2ff2e9e3b..06722492f 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -152,6 +152,54 @@ object Checking { else info } } + + /** Check that refinement satisfies the following two conditions + * 1. No part of it refers to a symbol that's defined in the same refinement + * at a textually later point. + * 2. All references to the refinement itself via `this` are followed by + * selections. + * Note: It's not yet clear what exactly we want to allow and what we want to rule out. + * This depends also on firming up the DOT calculus. For the moment we only issue + * deprecated warnings, not errors. + */ + def checkRefinementNonCyclic(refinement: Tree, refineCls: ClassSymbol)(implicit ctx: Context): Unit = { + def flag(what: String, tree: Tree) = + ctx.deprecationWarning(i"$what reference in refinement is deprecated", tree.pos) + def forwardRef(tree: Tree) = flag("forward", tree) + def selfRef(tree: Tree) = flag("self", tree) + val checkTree = new TreeAccumulator[Unit] { + def checkRef(tree: Tree, sym: Symbol) = + if (sym.maybeOwner == refineCls && tree.pos.start <= sym.pos.end) forwardRef(tree) + def apply(x: Unit, tree: Tree) = tree match { + case tree @ Select(This(_), _) => + checkRef(tree, tree.symbol) + case tree: RefTree => + checkRef(tree, tree.symbol) + foldOver(x, tree) + case tree: This => + selfRef(tree) + case tree: TypeTree => + val checkType = new TypeAccumulator[Unit] { + def apply(x: Unit, tp: Type): Unit = tp match { + case tp: NamedType => + checkRef(tree, tp.symbol) + tp.prefix match { + case pre: ThisType => + case pre => foldOver(x, pre) + } + case tp: ThisType if tp.cls == refineCls => + selfRef(tree) + case _ => + foldOver(x, tp) + } + } + checkType((), tree.tpe) + case _ => + foldOver(x, tree) + } + } + checkTree((), refinement) + } } trait Checking { diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 4bb6e172b..9ef73f0b6 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -20,6 +20,7 @@ import NameOps._ import Flags._ import Decorators._ import ErrorReporting._ +import Checking._ import EtaExpansion.etaExpand import dotty.tools.dotc.transform.Erasure.Boxing import util.Positions._ @@ -761,14 +762,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1") def addRefinement(parent: Type, refinement: Tree): Type = { typr.println(s"adding refinement $refinement") - def checkRef(tree: Tree, sym: Symbol) = - if (sym.maybeOwner == refineCls && tree.pos.start <= sym.pos.end) - ctx.error("illegal forward reference in refinement", tree.pos) - refinement foreachSubTree { - case tree: RefTree => checkRef(tree, tree.symbol) - case tree: TypeTree => checkRef(tree, tree.tpe.typeSymbol) - case _ => - } + checkRefinementNonCyclic(refinement, refineCls) val rsym = refinement.symbol val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, RefinedThis(rt))) |