From 0855b071d913e7acd5271ab34062c69e25cd93cd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 19 Mar 2016 19:40:47 +0100 Subject: Fixes to checkNonCyclic Simplified logic and now check prefixes of TypeRefs. Without the simplified logic we would get false cyclic errors for ski.scala. Test case: flowops.scala Fixes #1185. --- tests/pos/flowops.scala | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/pos/flowops.scala (limited to 'tests/pos') 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 +} -- cgit v1.2.3 From b49034715ac5dc5d3f8427b39e497d3e20c4c192 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 19 Mar 2016 19:46:47 +0100 Subject: Simplify and fix avoid logic The previous formulation broke for named parameters. Test case in flowops1.scala. --- src/dotty/tools/dotc/core/Types.scala | 8 ------- src/dotty/tools/dotc/typer/TypeAssigner.scala | 29 ++++++++--------------- tests/pos/flowops1.scala | 33 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 tests/pos/flowops1.scala (limited to 'tests/pos') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 3801f1914..ffb662490 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -841,14 +841,6 @@ object Types { case _ => this } - /** If this is a refinement type, the unrefined parent, - * else the type itself. - */ - final def unrefine(implicit ctx: Context): Type = stripTypeVar match { - case tp @ RefinedType(tycon, _) => tycon.unrefine - case _ => this - } - /** If this is a (possibly aliased, annotated, and/or parameterized) reference to * a class, the class type ref, otherwise NoType. * @param refinementOK If `true` we also skip non-parameter refinements. diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 84951fd2b..840346536 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -6,6 +6,7 @@ import core._ import ast._ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ +import TypeApplications.AppliedType import util.Positions._ import config.Printers._ import ast.Trees._ @@ -93,27 +94,17 @@ trait TypeAssigner { case _ => mapOver(tp) } + case tp @ AppliedType(tycon, args) if toAvoid(tycon) => + val base = apply(tycon) + apply(base.appliedTo(tp.baseArgInfos(base.typeSymbol))) case tp @ RefinedType(parent, name) if variance > 0 => - // The naive approach here would be to first approximate the parent, - // but if the base type of the approximated parent is different from - // the current base type, then the current refinement won't be valid - // if it's a type parameter refinement. - // Therefore we first approximate the base type, then use `baseArgInfos` - // to get correct refinements for the approximated base type, then - // recursively approximate the resulting type. - val base = tp.unrefine - if (toAvoid(base)) { - val base1 = apply(base) - apply(base1.appliedTo(tp.baseArgInfos(base1.typeSymbol))) + val parent1 = apply(tp.parent) + val refinedInfo1 = apply(tp.refinedInfo) + if (toAvoid(refinedInfo1)) { + typr.println(s"dropping refinement from $tp") + parent1 } else { - val parent1 = apply(tp.parent) - val refinedInfo1 = apply(tp.refinedInfo) - if (toAvoid(refinedInfo1)) { - typr.println(s"dropping refinement from $tp") - parent1 - } else { - tp.derivedRefinedType(parent1, name, refinedInfo1) - } + tp.derivedRefinedType(parent1, name, refinedInfo1) } case tp: TypeVar if ctx.typerState.constraint.contains(tp) => val lo = ctx.typerState.constraint.fullLowerBound(tp.origin) diff --git a/tests/pos/flowops1.scala b/tests/pos/flowops1.scala new file mode 100644 index 000000000..2acd515af --- /dev/null +++ b/tests/pos/flowops1.scala @@ -0,0 +1,33 @@ +object Test { + class NotUsed + + trait FO[type +Out, type +Mat] { self => + type Repr <: FO[Mat = self.Mat] { + type Repr = self.Repr + } + def map[T](f: Out => T): Repr[Out = T] = ??? + } + + class Source[type +Out, type +Mat] extends FO[Out, Mat] { + type Repr <: Source[Out, Mat] + } + + class Flow[type -In, type +Out, type +Mat] extends FO[Out, Mat] { + type Repr <: Flow[In, Out, Mat] + } + + implicit class x[O, M, F <: FO](val f: F[Out = O, Mat = M]) extends AnyVal { + def xx(i: Int): f.Repr[Out = O] = f.map(identity) + } + + val s1 = new Source[Int, NotUsed].xx(12) + val s2: Source[Int, NotUsed] = s1 + val f1 = x[Int, NotUsed, Flow[In = Int]](new Flow[Int, Int, NotUsed]).xx(12) + val f2: Flow[Int, Int, NotUsed] = f1 + + + val f3 = x(new Flow[Int, Int, NotUsed]).xx(12) + val f4: Flow[Int, Int, NotUsed] = f3 + val f5 = new Flow[Int, Int, NotUsed].xx(12) + val f6: Flow[Int, Int, NotUsed] = f5 +} -- cgit v1.2.3 From bbbb6620dabb2a247f74e4cdfbffd178654decba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 22 Mar 2016 12:26:24 +0100 Subject: Fix test case. The intent is that Repr implementations should not bind the Out parameter. --- tests/pos/flowops1.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tests/pos') diff --git a/tests/pos/flowops1.scala b/tests/pos/flowops1.scala index 2acd515af..7cf8eb6ef 100644 --- a/tests/pos/flowops1.scala +++ b/tests/pos/flowops1.scala @@ -8,12 +8,12 @@ object Test { def map[T](f: Out => T): Repr[Out = T] = ??? } - class Source[type +Out, type +Mat] extends FO[Out, Mat] { - type Repr <: Source[Out, Mat] + class Source[type +Out, type +Mat] extends FO[Out, Mat] { self => + type Repr <: Source[Mat = self.Mat] } - class Flow[type -In, type +Out, type +Mat] extends FO[Out, Mat] { - type Repr <: Flow[In, Out, Mat] + class Flow[type -In, type +Out, type +Mat] extends FO[Out, Mat] { self => + type Repr <: Flow[In = self.In, Mat = self.Mat] } implicit class x[O, M, F <: FO](val f: F[Out = O, Mat = M]) extends AnyVal { -- cgit v1.2.3 From 6d2a3d341128eddb99c0f52bca154f9e8b87eb55 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 25 Mar 2016 10:46:07 +0100 Subject: Test cases --- tests/pos/flowops1.scala | 6 ++++++ tests/pos/named-params.scala | 48 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) (limited to 'tests/pos') diff --git a/tests/pos/flowops1.scala b/tests/pos/flowops1.scala index 7cf8eb6ef..649a9b18c 100644 --- a/tests/pos/flowops1.scala +++ b/tests/pos/flowops1.scala @@ -20,6 +20,10 @@ object Test { def xx(i: Int): f.Repr[Out = O] = f.map(identity) } + class xalt[O, M, F <: FO](val f: F[Out = O, Mat = M]) extends AnyVal { + def xx(i: Int): FO[Out = O, Mat = M] = ??? + } + val s1 = new Source[Int, NotUsed].xx(12) val s2: Source[Int, NotUsed] = s1 val f1 = x[Int, NotUsed, Flow[In = Int]](new Flow[Int, Int, NotUsed]).xx(12) @@ -30,4 +34,6 @@ object Test { val f4: Flow[Int, Int, NotUsed] = f3 val f5 = new Flow[Int, Int, NotUsed].xx(12) val f6: Flow[Int, Int, NotUsed] = f5 + val f7 = new xalt(new Flow[Int, Int, NotUsed]).xx(12) + val f8: FO[Int, NotUsed] = f7 } diff --git a/tests/pos/named-params.scala b/tests/pos/named-params.scala index bcd64ea4b..3fab24cd2 100644 --- a/tests/pos/named-params.scala +++ b/tests/pos/named-params.scala @@ -29,9 +29,39 @@ object Test { val z3 = d2[E = Int](1) val z4 = d2[V = Int]("AAA") val z5 = d2[E = Int][V = String](1) + +// Testing type inference + + def f[X <: C](x: X[Int, Int]): X[String, String] = ??? + val arg1: C[Int, Int] = ??? + val res1 = f(arg1) + val chk1: C[String, String] = res1 + + class C1[type Elem, type Value](x: Elem) extends C[Elem, Value](x) + class CC extends C1[Int, Int](1) + val arg2: CC = ??? + val res2 = f(arg2) + val chk2: C[String, String] = res2 + + class D1[type Elem, type Value](x: Elem) extends C[Elem, Value](x) + class DD extends D1[Int, Int](2) + val arg3: CC & DD = ??? + val res3 = f(arg3) + val chk3: (C1 & D1) { type Elem = String; type Value = String } = res3 + val arg4: CC | DD = ??? + val res4 = f(arg4) + val chk4: C[String, String] = ??? + + class CX[type Elem](x: Elem) extends C1[Elem, Int](x) + class DX[type Value]() extends D1[Int, Value](2) + val arg5: CX[Int] & DX[Int] = ??? + val res5 = f(arg5) + val chk5: (C1 & D1) { type Elem = String; type Value = String } = res5 + val chk6: C1[String, String] & D1[String, String] = chk5 + val chk7: (C1 & D1) { type Elem = String; type Value = String } = chk6 } -// Adapated from i94-nada +// Adapted from i94-nada, somewhat non-sensical trait Test1 { trait Monad[type Elem] { def unit: Elem @@ -40,7 +70,21 @@ trait Test1 { case class Left[A,B](unit: A) extends Either[A,B] with Monad[A] case class Right[A,B](unit: B) extends Either[A,B] with Monad[B] def flatMap[X,Y,M <: Monad](m: M[Elem = X], f: X => M[Elem = Y]): M[Elem = Y] = f(m.unit) - println(flatMap(Left(1), {x: Int => Left(x)})) + val res = flatMap(Left(1), {x: Int => Left(x)}) + val chk: Either[Int, Nothing] & Monad & Product1[Int] = res +} + +// Adapted from i94-nada, this time with more sense +trait Test2 { + trait Monad[type Elem] { + def unit: Elem + } + sealed abstract class Either[A,B] + case class Left[type Elem, B](unit: Elem) extends Either[Elem,B] with Monad[Elem] + case class Right[A, type Elem](unit: Elem) extends Either[A,Elem] with Monad[Elem] + def flatMap[X,Y,M <: Monad](m: M[Elem = X], f: X => M[Elem = Y]): M[Elem = Y] = f(m.unit) + val res = flatMap(Left(1), {x: Int => Left(x)}) + val chk: Left[Int, Nothing] = res } -- cgit v1.2.3 From f675ad9507089f8b912357fab86740653c1b8789 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 30 Mar 2016 12:16:03 +0200 Subject: Domain checking for named type parameters Now verifies that the named type parameters of an overriding type or class are the same as the named type parameters of an overridden type. --- src/dotty/tools/dotc/typer/RefChecks.scala | 5 +++++ tests/neg/named-params.scala | 3 +++ tests/pos/CollectionStrawMan3.scala | 20 ++++++++++---------- 3 files changed, 18 insertions(+), 10 deletions(-) (limited to 'tests/pos') diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index afbb43faf..37c2fe48f 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -246,6 +246,8 @@ object RefChecks { isDefaultGetter(member.name) || // default getters are not checked for compatibility memberTp.overrides(otherTp) + def domain(sym: Symbol): Set[Name] = sym.info.namedTypeParams.map(_.name) + //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG // return if we already checked this combination elsewhere @@ -342,6 +344,9 @@ object RefChecks { overrideError("cannot be used here - only term macros can override term macros") } else if (!compatibleTypes) { overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp)) + } else if (member.isType && domain(member) != domain(other)) { + overrideError("has different named type parameters: "+ + i"[${domain(member).toList}%, %] instead of [${domain(other).toList}%, %]") } else { checkOverrideDeprecated() } diff --git a/tests/neg/named-params.scala b/tests/neg/named-params.scala index 9ef4ed066..5a2375b15 100644 --- a/tests/neg/named-params.scala +++ b/tests/neg/named-params.scala @@ -32,3 +32,6 @@ object Test { val z5 = d2[Elem = Int][Value = String](1) //error // error } + + + diff --git a/tests/pos/CollectionStrawMan3.scala b/tests/pos/CollectionStrawMan3.scala index 47d3b52d6..c21a73f00 100644 --- a/tests/pos/CollectionStrawMan3.scala +++ b/tests/pos/CollectionStrawMan3.scala @@ -114,13 +114,13 @@ object CollectionStrawMan1 { /* --------- Concrete collection types ------------------------------- */ /** Concrete collection type: List */ - sealed trait List[+A] extends Seq[A] with FromIterator[List] { + sealed trait List[type +Elem] extends Seq[Elem] with FromIterator[List] { def isEmpty: Boolean - def head: A - def tail: List[A] - def iterator = new ListIterator[A](this) + def head: Elem + def tail: List[Elem] + def iterator = new ListIterator[Elem](this) def fromIterator[B](it: Iterator[B]): List[B] = List.fromIterator(it) - def apply(i: Int): A = { + def apply(i: Int): Elem = { require(!isEmpty) if (i == 0) head else tail.apply(i - 1) } @@ -155,17 +155,17 @@ object CollectionStrawMan1 { } /** Concrete collection type: ArrayBuffer */ - class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + class ArrayBuffer[type Elem] private (initElems: Array[AnyRef], initLength: Int) extends Seq[Elem] with FromIterator[ArrayBuffer] { def this() = this(new Array[AnyRef](16), 0) private var elems: Array[AnyRef] = initElems private var start = 0 private var limit = initLength - def apply(i: Int) = elems(start + i).asInstanceOf[A] + def apply(i: Int) = elems(start + i).asInstanceOf[Elem] def length = limit - start - def iterator = new ArrayBufferIterator[A](elems, start, length) + def iterator = new ArrayBufferIterator[Elem](elems, start, length) def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = ArrayBuffer.fromIterator(it) - def +=(elem: A): this.type = { + def +=(elem: Elem): this.type = { if (limit == elems.length) { if (start > 0) { Array.copy(elems, start, elems, 0, length) @@ -213,7 +213,7 @@ object CollectionStrawMan1 { } /** Concrete collection type: View */ - class View[+A](it: => Iterator[A]) extends CanIterate[A] { + class View[type +Elem](it: => Iterator[Elem]) extends CanIterate[Elem] { def iterator = it } -- cgit v1.2.3