Skip to content

Commit 71e3133

Browse files
committed
Eta expand $apply projected types if needed
It turns out that asSeenFrom can produce types that get projected with $apply but that are not higher-kinded. An exampple failure is in Iter3, andother in scala.collection.immutable.Map (which is now part of the test suite). We now detect that situation, and eta expand the projected type in `derivedSelect`, this will force a subssequent `lookupRefined` which will give the desired normalized type. Also added is a configurable test that checks that $apply projected tyeps are in fact higher-kinded.
1 parent 154f351 commit 71e3133

File tree

7 files changed

+677
-16
lines changed

7 files changed

+677
-16
lines changed

src/dotty/tools/dotc/config/Config.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ object Config {
7171
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
7272
final val fastPathForRefinedSubtype = true
7373

74+
/** If this flag is set, $apply projections are checked that they apply to a
75+
* higher-kinded type.
76+
*/
77+
final val checkProjections = false
78+
7479
/** When set, use new signature-based matching.
7580
* Advantage of doing so: It's supposed to be faster
7681
* Disadvantage: It might hide inconsistencies, so while debugging it's better to turn it off

src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,27 @@ class TypeApplications(val self: Type) extends AnyVal {
155155
case _ => false
156156
}
157157

158+
/** True if it can be determined without forcing that the class symbol
159+
* of this application exists and is not a lambda trait.
160+
* Equivalent to
161+
*
162+
* self.classSymbol.exists && !self.classSymbol.isLambdaTrait
163+
*
164+
* but without forcing anything.
165+
*/
166+
def noHK(implicit ctx: Context): Boolean = self.stripTypeVar match {
167+
case self: RefinedType =>
168+
self.parent.noHK
169+
case self: TypeRef =>
170+
(self.denot.exists) && {
171+
val sym = self.symbol
172+
if (sym.isClass) !sym.isLambdaTrait
173+
else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.noHK
174+
}
175+
case _ =>
176+
false
177+
}
178+
158179
/** Encode the type resulting from applying this type to given arguments */
159180
final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ {
160181
def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match {
@@ -510,6 +531,14 @@ class TypeApplications(val self: Type) extends AnyVal {
510531
if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand
511532
else self
512533

534+
/** Eta expand the prefix in front of any refinements. */
535+
def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match {
536+
case self: RefinedType =>
537+
self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo)
538+
case _ =>
539+
self.EtaExpand
540+
}
541+
513542
/** If `self` is a (potentially partially instantiated) eta expansion of type T, return T,
514543
* otherwise NoType. More precisely if `self` is of the form
515544
*

src/dotty/tools/dotc/core/Types.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,7 +1487,9 @@ object Types {
14871487
if (prefix eq this.prefix) this
14881488
else {
14891489
val res = prefix.lookupRefined(name)
1490-
if (res.exists) res else newLikeThis(prefix)
1490+
if (res.exists) res
1491+
else if (name == tpnme.hkApply && prefix.noHK) derivedSelect(prefix.EtaExpandCore)
1492+
else newLikeThis(prefix)
14911493
}
14921494

14931495
/** Create a NamedType of the same kind as this type, but with a new prefix.
@@ -1725,9 +1727,15 @@ object Types {
17251727
}
17261728

17271729
object TypeRef {
1730+
def checkProjection(prefix: Type, name: TypeName)(implicit ctx: Context) =
1731+
if (name == tpnme.hkApply && prefix.noHK)
1732+
assert(false, s"bad type : $prefix.$name should not be $$applied")
1733+
17281734
/** Create type ref with given prefix and name */
1729-
def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef =
1735+
def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = {
1736+
if (Config.checkProjections) checkProjection(prefix, name)
17301737
ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TypeRef]
1738+
}
17311739

17321740
/** Create type ref to given symbol */
17331741
def apply(prefix: Type, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
@@ -1736,8 +1744,10 @@ object Types {
17361744
/** Create a non-member type ref (which cannot be reloaded using `member`),
17371745
* with given prefix, name, and symbol.
17381746
*/
1739-
def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
1747+
def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = {
1748+
if (Config.checkProjections) checkProjection(prefix, name)
17401749
unique(new TypeRefWithFixedSym(prefix, name, sym))
1750+
}
17411751

17421752
/** Create a type ref referring to given symbol with given name.
17431753
* This is very similar to TypeRef(Type, Symbol),
@@ -3198,7 +3208,9 @@ object Types {
31983208

31993209
class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError(
32003210
i"""cannot resolve reference to type $pre.$name
3201-
|the classfile defining the type might be missing from the classpath${otherReason(pre)}""".stripMargin)
3211+
|the classfile defining the type might be missing from the classpath${otherReason(pre)}""".stripMargin) {
3212+
printStackTrace()
3213+
}
32023214

32033215
private def otherReason(pre: Type)(implicit ctx: Context): String = pre match {
32043216
case pre: ThisType if pre.givenSelfType.exists =>

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,8 @@ object Checking {
115115
val parent1 = this(parent)
116116
val saved = cycleOK
117117
cycleOK = nestedCycleOK
118-
119-
/** A derived refined type with two possible tweaks:
120-
* (1) LazyRefs in parents are pulled out,
121-
* (2) #Apply is added if the type is a fully applied type lambda.
122-
*/
123-
def derivedType(p: Type): Type = p match {
124-
case p: LazyRef => LazyRef(() => derivedType(p.ref))
125-
case _ =>
126-
val res = tp.derivedRefinedType(p, name, this(tp.refinedInfo))
127-
if (res.isSafeLambda && res.typeParams.isEmpty) res.select(tpnme.Apply) else res
128-
}
129-
try derivedType(parent1) finally cycleOK = saved
118+
try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo))
119+
finally cycleOK = saved
130120
case tp @ TypeRef(pre, name) =>
131121
try {
132122
// A prefix is interesting if it might contain (transitively) a reference
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/* __ *\
2+
** ________ ___ / / ___ Scala API **
3+
** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
4+
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
5+
** /____/\___/_/ |_/____/_/ | | **
6+
** |/ **
7+
\* */
8+
9+
10+
11+
package scala
12+
package collection
13+
package generic
14+
15+
import mutable.Builder
16+
import scala.annotation.migration
17+
import scala.annotation.unchecked.uncheckedVariance
18+
import scala.language.higherKinds
19+
20+
/** A template class for companion objects of ``regular`` collection classes
21+
* that represent an unconstrained higher-kinded type.
22+
*
23+
* @tparam A The type of the collection elements.
24+
* @tparam CC The type constructor representing the collection class.
25+
* @author Martin Odersky
26+
* @since 2.8
27+
* @define coll collection
28+
* @define Coll CC
29+
*/
30+
trait GenericTraversableTemplate[+A, +CC[X] <: GenTraversable[X]] extends HasNewBuilder[A, CC[A] @uncheckedVariance] {
31+
32+
/** Applies a function `f` to all elements of this $coll.
33+
*
34+
* @param f the function that is applied for its side-effect to every element.
35+
* The result of function `f` is discarded.
36+
*
37+
* @tparam U the type parameter describing the result of function `f`.
38+
* This result will always be ignored. Typically `U` is `Unit`,
39+
* but this is not necessary.
40+
*
41+
* @usecase def foreach(f: A => Unit): Unit
42+
*/
43+
def foreach[U](f: A => U): Unit
44+
45+
/** Selects the first element of this $coll.
46+
*
47+
* @return the first element of this $coll.
48+
* @throws `NoSuchElementException` if the $coll is empty.
49+
*/
50+
def head: A
51+
52+
/** Tests whether this $coll is empty.
53+
*
54+
* @return `true` if the $coll contain no elements, `false` otherwise.
55+
*/
56+
def isEmpty: Boolean
57+
58+
/** The factory companion object that builds instances of class $Coll.
59+
* (or its `Iterable` superclass where class $Coll is not a `Seq`.)
60+
*/
61+
def companion: GenericCompanion[CC]
62+
63+
/** The builder that builds instances of type $Coll[A]
64+
*/
65+
protected[this] def newBuilder: Builder[A, CC[A]] = companion.newBuilder[A]
66+
67+
/** The generic builder that builds instances of $Coll
68+
* at arbitrary element types.
69+
*/
70+
def genericBuilder[B]: Builder[B, CC[B]] = companion.newBuilder[B]
71+
72+
private def sequential: TraversableOnce[A] = this.asInstanceOf[GenTraversableOnce[A]].seq
73+
74+
/** Converts this $coll of pairs into two collections of the first and second
75+
* half of each pair.
76+
*
77+
* {{{
78+
* val xs = $Coll(
79+
* (1, "one"),
80+
* (2, "two"),
81+
* (3, "three")).unzip
82+
* // xs == ($Coll(1, 2, 3),
83+
* // $Coll(one, two, three))
84+
* }}}
85+
*
86+
* @tparam A1 the type of the first half of the element pairs
87+
* @tparam A2 the type of the second half of the element pairs
88+
* @param asPair an implicit conversion which asserts that the element type
89+
* of this $coll is a pair.
90+
* @return a pair of ${coll}s, containing the first, respectively second
91+
* half of each element pair of this $coll.
92+
*/
93+
def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) = {
94+
val b1 = genericBuilder[A1]
95+
val b2 = genericBuilder[A2]
96+
for (xy <- sequential) {
97+
val (x, y) = asPair(xy)
98+
b1 += x
99+
b2 += y
100+
}
101+
(b1.result(), b2.result())
102+
}
103+
104+
/** Converts this $coll of triples into three collections of the first, second,
105+
* and third element of each triple.
106+
*
107+
* {{{
108+
* val xs = $Coll(
109+
* (1, "one", '1'),
110+
* (2, "two", '2'),
111+
* (3, "three", '3')).unzip3
112+
* // xs == ($Coll(1, 2, 3),
113+
* // $Coll(one, two, three),
114+
* // $Coll(1, 2, 3))
115+
* }}}
116+
*
117+
* @tparam A1 the type of the first member of the element triples
118+
* @tparam A2 the type of the second member of the element triples
119+
* @tparam A3 the type of the third member of the element triples
120+
* @param asTriple an implicit conversion which asserts that the element type
121+
* of this $coll is a triple.
122+
* @return a triple of ${coll}s, containing the first, second, respectively
123+
* third member of each element triple of this $coll.
124+
*/
125+
def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = {
126+
val b1 = genericBuilder[A1]
127+
val b2 = genericBuilder[A2]
128+
val b3 = genericBuilder[A3]
129+
130+
for (xyz <- sequential) {
131+
val (x, y, z) = asTriple(xyz)
132+
b1 += x
133+
b2 += y
134+
b3 += z
135+
}
136+
(b1.result(), b2.result(), b3.result())
137+
}
138+
139+
/** Converts this $coll of traversable collections into
140+
* a $coll formed by the elements of these traversable
141+
* collections.
142+
*
143+
* @tparam B the type of the elements of each traversable collection.
144+
* @param asTraversable an implicit conversion which asserts that the element
145+
* type of this $coll is a `GenTraversable`.
146+
* @return a new $coll resulting from concatenating all element ${coll}s.
147+
*
148+
* @usecase def flatten[B]: $Coll[B]
149+
*
150+
* @inheritdoc
151+
*
152+
* The resulting collection's type will be guided by the
153+
* static type of $coll. For example:
154+
*
155+
* {{{
156+
* val xs = List(
157+
* Set(1, 2, 3),
158+
* Set(1, 2, 3)
159+
* ).flatten
160+
* // xs == List(1, 2, 3, 1, 2, 3)
161+
*
162+
* val ys = Set(
163+
* List(1, 2, 3),
164+
* List(3, 2, 1)
165+
* ).flatten
166+
* // ys == Set(1, 2, 3)
167+
* }}}
168+
*/
169+
def flatten[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]): CC[B] = {
170+
val b = genericBuilder[B]
171+
for (xs <- sequential)
172+
b ++= asTraversable(xs).seq
173+
b.result()
174+
}
175+
176+
/** Transposes this $coll of traversable collections into
177+
* a $coll of ${coll}s.
178+
*
179+
* The resulting collection's type will be guided by the
180+
* static type of $coll. For example:
181+
*
182+
* {{{
183+
* val xs = List(
184+
* Set(1, 2, 3),
185+
* Set(4, 5, 6)).transpose
186+
* // xs == List(
187+
* // List(1, 4),
188+
* // List(2, 5),
189+
* // List(3, 6))
190+
*
191+
* val ys = Vector(
192+
* List(1, 2, 3),
193+
* List(4, 5, 6)).transpose
194+
* // ys == Vector(
195+
* // Vector(1, 4),
196+
* // Vector(2, 5),
197+
* // Vector(3, 6))
198+
* }}}
199+
*
200+
* @tparam B the type of the elements of each traversable collection.
201+
* @param asTraversable an implicit conversion which asserts that the
202+
* element type of this $coll is a `Traversable`.
203+
* @return a two-dimensional $coll of ${coll}s which has as ''n''th row
204+
* the ''n''th column of this $coll.
205+
* @throws `IllegalArgumentException` if all collections in this $coll
206+
* are not of the same size.
207+
*/
208+
@migration("`transpose` throws an `IllegalArgumentException` if collections are not uniformly sized.", "2.9.0")
209+
def transpose[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]): CC[CC[B] @uncheckedVariance] = {
210+
if (isEmpty)
211+
return genericBuilder[CC[B]].result()
212+
213+
def fail = throw new IllegalArgumentException("transpose requires all collections have the same size")
214+
215+
val headSize = asTraversable(head).size
216+
val bs: IndexedSeq[Builder[B, CC[B]]] = IndexedSeq.fill(headSize)(genericBuilder[B])
217+
for (xs <- sequential) {
218+
var i = 0
219+
for (x <- asTraversable(xs)) {
220+
if (i >= headSize) fail
221+
bs(i) += x
222+
i += 1
223+
}
224+
if (i != headSize)
225+
fail
226+
}
227+
val bb = genericBuilder[CC[B]]
228+
for (b <- bs) bb += b.result
229+
bb.result()
230+
}
231+
}
232+

0 commit comments

Comments
 (0)