diff options
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 38 | ||||
-rw-r--r-- | tests/pos/flowops.scala | 31 |
2 files changed, 49 insertions, 20 deletions
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 9b1f756b7..22d2407bc 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -156,16 +156,24 @@ object Checking { tp } - def apply(tp: Type) = tp match { + private def apply(tp: Type, cycleOK: Boolean, nestedCycleOK: Boolean): Type = { + val savedCycleOK = this.cycleOK + val savedNestedCycleOK = this.nestedCycleOK + this.cycleOK = cycleOK + this.nestedCycleOK = nestedCycleOK + try apply(tp) + finally { + this.cycleOK = savedCycleOK + this.nestedCycleOK = savedNestedCycleOK + } + } + + def apply(tp: Type): Type = tp match { case tp: TermRef => this(tp.info) mapOver(tp) case tp @ RefinedType(parent, name) => - val parent1 = this(parent) - val saved = cycleOK - cycleOK = nestedCycleOK - try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo)) - finally cycleOK = saved + tp.derivedRefinedType(this(parent), name, this(tp.refinedInfo, nestedCycleOK, nestedCycleOK)) case tp @ TypeRef(pre, name) => try { // A prefix is interesting if it might contain (transitively) a reference @@ -182,19 +190,12 @@ object Checking { case _: RefinedType => true case _ => false } - // If prefix is interesting, check info of typeref recursively, marking the referred symbol - // with NoCompleter. This provokes a CyclicReference when the symbol - // is hit again. Without this precaution we could stackoverflow here. if (isInteresting(pre)) { - val info = tp.info - val sym = tp.symbol - if (sym.infoOrCompleter == SymDenotations.NoCompleter) throw CyclicReference(sym) - val symInfo = sym.info - if (sym.exists) sym.info = SymDenotations.NoCompleter - try checkInfo(info) - finally if (sym.exists) sym.info = symInfo + val pre1 = this(pre, false, false) + checkInfo(tp.info) + if (pre1 eq pre) tp else tp.newLikeThis(pre1) } - tp + else tp } catch { case ex: CyclicReference => ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK") @@ -210,9 +211,6 @@ object Checking { * @pre sym is not yet initialized (i.e. its type is a Completer). * @return `info` where every legal F-bounded reference is proctected * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported. - * Furthermore: Add an #Apply to a fully instantiated type lambda, if none was - * given before. This is necessary here because sometimes type lambdas are not - * recognized when they are first formed. */ def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = { val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.addMode(Mode.CheckCyclic)) diff --git a/tests/pos/flowops.scala b/tests/pos/flowops.scala new file mode 100644 index 000000000..6aead26be --- /dev/null +++ b/tests/pos/flowops.scala @@ -0,0 +1,31 @@ +object Test { + import language.higherKinds + + class NotUsed + + trait FO[+Out, +Mat] { self => + type Repr[+O] <: FO[O, Mat] { + type Repr[+OO] = self.Repr[OO] + } + def map[T](f: Out => T): Repr[T] = ??? + } + + class Source[+O, +M] extends FO[O, M] { + type Repr[+OO] <: Source[OO, M] + } + + class Flow[-I, +O, +M] extends FO[O, M] { + type Repr[+OO] <: Flow[I, OO, M] + } + + implicit class x[O, M, F[o, m] <: FO[o, m]](val f: F[O, M]) extends AnyVal { + def xx(i: Int): f.Repr[O] = f.map(identity) + } + + type IntFlow[O, M] = Flow[Int, O, M] + + val s1 = new Source[Int, NotUsed].xx(12) + val s2: Source[Int, NotUsed] = s1 + val f1 = x[Int, NotUsed, IntFlow](new Flow[Int, Int, NotUsed]).xx(12) + val f2: Flow[Int, Int, NotUsed] = f1 +} |