Skip to content

Commit 0514d0e

Browse files
committed
Merge pull request #1219 from dotty-staging/fix-strawmans
Fix strawmans
2 parents 247e913 + 79a284f commit 0514d0e

18 files changed

+2222
-48
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,21 @@ object desugar {
256256
// prefixed by type or val). `tparams` and `vparamss` are the type parameters that
257257
// go in `constr`, the constructor after desugaring.
258258

259+
/** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */
260+
def isAnyVal(tree: Tree): Boolean = tree match {
261+
case Ident(tpnme.AnyVal) => true
262+
case Select(qual, tpnme.AnyVal) => isScala(qual)
263+
case _ => false
264+
}
265+
def isScala(tree: Tree): Boolean = tree match {
266+
case Ident(nme.scala_) => true
267+
case Select(Ident(nme.ROOTPKG), nme.scala_) => true
268+
case _ => false
269+
}
270+
259271
val isCaseClass = mods.is(Case) && !mods.is(Module)
272+
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
273+
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
260274

261275
val constrTparams = constr1.tparams map toDefParam
262276
val constrVparamss =
@@ -398,7 +412,9 @@ object desugar {
398412
companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
399413
}
400414
else if (defaultGetters.nonEmpty)
401-
companionDefs(anyRef, defaultGetters)
415+
companionDefs(anyRef, defaultGetters)
416+
else if (isValueClass)
417+
companionDefs(anyRef, Nil)
402418
else Nil
403419

404420

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,12 @@ object NameOps {
116116
name.drop(tpnme.hkArgPrefixLength).toString.toInt
117117

118118
def isLambdaTraitName(implicit ctx: Context): Boolean =
119-
name.startsWith(tpnme.hkLambdaPrefix)
119+
name.isTypeName && name.startsWith(tpnme.hkLambdaPrefix)
120+
121+
def lambdaTraitVariances(implicit ctx: Context): List[Int] = {
122+
val vs = name.drop(tpnme.hkLambdaPrefix.length)
123+
vs.toList.map(c => tpnme.varianceSuffixes.indexOf(c) - 1)
124+
}
120125

121126
/** If the name ends with $nn where nn are
122127
* all digits, strip the $ and the digits.
@@ -179,7 +184,13 @@ object NameOps {
179184
* an encoded name, e.g. super$$plus$eq. See #765.
180185
*/
181186
def unexpandedName: N = {
182-
val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
187+
var idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
188+
189+
// Hack to make super accessors from traits work. They would otherwise fail because of #765
190+
// TODO: drop this once we have more robust name handling
191+
if (name.slice(idx - FalseSuperLength, idx) == FalseSuper)
192+
idx -= FalseSuper.length
193+
183194
if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N]
184195
}
185196

@@ -431,4 +442,7 @@ object NameOps {
431442
name.dropRight(nme.LAZY_LOCAL.length)
432443
}
433444
}
445+
446+
private final val FalseSuper = "$$super".toTermName
447+
private val FalseSuperLength = FalseSuper.length
434448
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ object Names {
3737
*/
3838
abstract class Name extends DotClass
3939
with PreName
40-
with Seq[Char]
40+
with collection.immutable.Seq[Char]
4141
with IndexedSeqOptimized[Char, Name] {
4242

4343
/** A type for names of the same kind as this name */

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,14 @@ class TypeApplications(val self: Type) extends AnyVal {
377377
false
378378
}
379379

380+
/** Dealias type if it can be done without forcing anything */
381+
def safeDealias(implicit ctx: Context): Type = self match {
382+
case self: TypeRef if self.denot.exists && self.symbol.isAliasType =>
383+
self.info.bounds.hi.stripTypeVar.safeDealias
384+
case _ =>
385+
self
386+
}
387+
380388
/** Replace references to type parameters with references to hk arguments `this.$hk_i`
381389
* Care is needed not to cause cyclic reference errors, hence `SafeSubstMap`.
382390
*/
@@ -546,8 +554,8 @@ class TypeApplications(val self: Type) extends AnyVal {
546554
substHkArgs(body)
547555
case self: PolyType =>
548556
self.instantiate(args)
549-
case _ =>
550-
appliedTo(args, typeParams)
557+
case self1 =>
558+
self1.safeDealias.appliedTo(args, typeParams)
551559
}
552560
}
553561

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,9 @@ object Types {
903903
case pre: RefinedType =>
904904
object instantiate extends TypeMap {
905905
var isSafe = true
906-
def apply(tp: Type): Type = tp match {
906+
def apply(tp: Type): Type =
907+
if (!isSafe) tp
908+
else tp match {
907909
case TypeRef(RefinedThis(`pre`), name) if name.isHkArgName =>
908910
member(name).info match {
909911
case TypeAlias(alias) => alias
@@ -2061,7 +2063,7 @@ object Types {
20612063
false
20622064
}
20632065
override def computeHash = doHash(refinedName, refinedInfo, parent)
2064-
override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)"
2066+
override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)"
20652067
}
20662068

20672069
class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) {

src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
252252
readPackageRef().termRef
253253
case TYPEREF =>
254254
val name = readName().toTypeName
255+
if (name.isLambdaTraitName) // Make sure curresponding lambda trait exists
256+
defn.LambdaTrait(name.lambdaTraitVariances)
255257
TypeRef(readType(), name)
256258
case TERMREF =>
257259
readNameSplitSig() match {

src/dotty/tools/dotc/transform/ExtensionMethods.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import SymUtils._
3333
* This is different from the implementation of value classes in Scala 2
3434
* (see SIP-15) which uses `asInstanceOf` which does not typecheck.
3535
*/
36-
class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization with NeedsCompanions { thisTransformer =>
36+
class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer =>
3737

3838
import tpd._
3939
import ExtensionMethods._
@@ -45,10 +45,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
4545

4646
override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist
4747

48-
def isCompanionNeeded(cls: ClassSymbol)(implicit ctx: Context): Boolean = {
49-
isDerivedValueClass(cls)
50-
}
51-
5248
override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
5349
case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass =>
5450
moduleClassSym.linkedClass match {

src/dotty/tools/dotc/transform/FirstTransform.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
4444
this
4545
}
4646

47-
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = {
47+
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp/*{
4848
tp match {
4949
//create companions for value classes that are not from currently compiled source file
5050
case tp@ClassInfo(_, cls, _, decls, _)
@@ -59,7 +59,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
5959
case _ => tp
6060
}
6161
}
62-
62+
*/
6363
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
6464
tree match {
6565
case Select(qual, _) if tree.symbol.exists =>

src/dotty/tools/dotc/transform/SyntheticMethods.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import DenotTransformers._
1010
import ast.Trees._
1111
import ast.untpd
1212
import Decorators._
13+
import NameOps._
1314
import ValueClasses.isDerivedValueClass
1415
import scala.collection.mutable.ListBuffer
1516
import scala.language.postfixOps
@@ -79,14 +80,17 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
7980
def forwardToRuntime(vrefss: List[List[Tree]]): Tree =
8081
ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head)
8182

83+
def ownName(vrefss: List[List[Tree]]): Tree =
84+
Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString))
85+
8286
def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match {
8387
case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody
8488
case nme.hashCode_ => vrefss => caseHashCodeBody
85-
case nme.toString_ => forwardToRuntime
89+
case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime
8690
case nme.equals_ => vrefss => equalsBody(vrefss.head.head)
8791
case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
8892
case nme.productArity => vrefss => Literal(Constant(accessors.length))
89-
case nme.productPrefix => vrefss => Literal(Constant(clazz.name.decode.toString))
93+
case nme.productPrefix => ownName
9094
}
9195
ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}")
9296
DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic)))

src/strawman/collections/CollectionStrawMan4.scala

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package strawman.collections
22

33
import Predef.{augmentString => _, wrapString => _, _}
44
import scala.reflect.ClassTag
5+
import annotation.unchecked.uncheckedVariance
6+
import annotation.tailrec
57

68
/** A strawman architecture for new collections. It contains some
79
* example collection classes and methods with the intent to expose
@@ -20,14 +22,7 @@ object CollectionStrawMan4 {
2022
def iterator: Iterator[A]
2123
}
2224

23-
/** Base trait for generic collections */
24-
trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
25-
def iterator: Iterator[A]
26-
def view: View[A] = View.fromIterator(iterator)
27-
def knownLength: Int = -1
28-
}
29-
30-
/** Base trait for instances that can construct a collection from an iterator */
25+
/** Base trait for instances that can construct a collection from an iterable */
3126
trait FromIterable[+C[X] <: Iterable[X]] {
3227
def fromIterable[B](v: Iterable[B]): C[B]
3328
}
@@ -38,16 +33,27 @@ object CollectionStrawMan4 {
3833
def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
3934
}
4035

36+
/** Base trait for generic collections */
37+
trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
38+
def view: View[A] = View.fromIterator(iterator) // view is overridden, cannot be defined in ops
39+
def knownLength: Int = -1
40+
}
41+
4142
/** Base trait for sequence collections */
4243
trait Seq[+A] extends Iterable[A] with FromIterable[Seq] {
4344
def apply(i: Int): A
4445
def length: Int
4546
}
4647

48+
/** Base trait for collection builders */
4749
trait Builder[-A, +To] {
4850
def +=(x: A): this.type
49-
def ++=(xs: IterableOnce[A]): Unit = xs.iterator.foreach(+=)
5051
def result: To
52+
53+
def ++=(xs: IterableOnce[A]): this.type = {
54+
xs.iterator.foreach(+=)
55+
this
56+
}
5157
}
5258

5359
/* ------------ Operations ----------------------------------- */
@@ -134,17 +140,18 @@ object CollectionStrawMan4 {
134140
require(!isEmpty)
135141
if (i == 0) head else tail.apply(i - 1)
136142
}
137-
def :::[B >: A](prefix: List[B]): List[B] =
138-
if (prefix.isEmpty) this
139-
else Cons(prefix.head, prefix.tail ::: this)
140143
def length: Int =
141144
if (isEmpty) 0 else 1 + tail.length
145+
def ++:[B >: A](prefix: List[B]): List[B] =
146+
if (prefix.isEmpty) this
147+
else Cons(prefix.head, prefix.tail ++: this)
142148
}
143149

144-
case class Cons[+A](x: A, xs: List[A]) extends List[A] {
150+
case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
151+
extends List[A] {
145152
def isEmpty = false
146153
def head = x
147-
def tail = xs
154+
def tail = next
148155
}
149156

150157
case object Nil extends List[Nothing] {
@@ -157,20 +164,64 @@ object CollectionStrawMan4 {
157164
def fromIterator[B](it: Iterator[B]): List[B] =
158165
if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil
159166
def fromIterable[B](c: Iterable[B]): List[B] = c match {
160-
case View.Concat(xs, ys: Iterable[B]) =>
161-
fromIterable(xs) ::: fromIterable(ys)
167+
case View.Concat(xs, ys: List[B]) =>
168+
fromIterable(xs) ++: ys
162169
case View.Drop(xs: List[B], n) =>
163-
var i = 0
164-
var ys = xs
165-
while (i < n && !xs.isEmpty) {
166-
ys = ys.tail
167-
i += 1
168-
}
169-
ys
170+
@tailrec def loop(xs: List[B], n: Int): List[B] =
171+
if (n > 0) loop(xs.tail, n - 1) else xs
172+
loop(xs, n)
173+
case c: List[B] => c
170174
case _ => fromIterator(c.iterator)
171175
}
172176
}
173177

178+
/** Concrete collection type: ListBuffer */
179+
class ListBuffer[A] extends Seq[A] with FromIterable[ListBuffer] with Builder[A, List[A]] {
180+
private var first, last: List[A] = Nil
181+
private var aliased = false
182+
def iterator = first.iterator
183+
def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
184+
def apply(i: Int) = first.apply(i)
185+
def length = first.length
186+
187+
private def copyElems(): Unit = {
188+
val buf = ListBuffer.fromIterable(result)
189+
first = buf.first
190+
last = buf.last
191+
aliased = false
192+
}
193+
def result = {
194+
aliased = true
195+
first
196+
}
197+
def +=(elem: A) = {
198+
if (aliased) copyElems()
199+
val last1 = Cons(elem, Nil)
200+
last match {
201+
case last: Cons[A] => last.next = last1
202+
case _ => first = last1
203+
}
204+
last = last1
205+
this
206+
}
207+
override def toString: String =
208+
if (first.isEmpty) "ListBuffer()"
209+
else {
210+
val b = new StringBuilder("ListBuffer(").append(first.head)
211+
first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
212+
}
213+
}
214+
215+
object ListBuffer extends IterableFactory[ListBuffer] {
216+
def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = coll match {
217+
case pd @ View.Partitioned(partition: View.Partition[B]) =>
218+
partition.distribute(new ListBuffer[B]())
219+
pd.forced.get.asInstanceOf[ListBuffer[B]]
220+
case _ =>
221+
new ListBuffer[B] ++= coll
222+
}
223+
}
224+
174225
/** Concrete collection type: ArrayBuffer */
175226
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
176227
extends Seq[A] with FromIterable[ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
@@ -234,12 +285,6 @@ object CollectionStrawMan4 {
234285
def apply(n: Int) = elems(start + n).asInstanceOf[A]
235286
}
236287

237-
case class StringView(s: String) extends RandomAccessView[Char] {
238-
val start = 0
239-
val end = s.length
240-
def apply(n: Int) = s.charAt(n)
241-
}
242-
243288
/** Concrete collection type: String */
244289
implicit class StringOps(val s: String) extends AnyVal with Ops[Char] {
245290
def iterator: Iterator[Char] = new StringView(s).iterator
@@ -277,6 +322,12 @@ object CollectionStrawMan4 {
277322
def ++(xs: String): String = s + xs
278323
}
279324

325+
case class StringView(s: String) extends RandomAccessView[Char] {
326+
val start = 0
327+
val end = s.length
328+
def apply(n: Int) = s.charAt(n)
329+
}
330+
280331
/* ------------ Views --------------------------------------- */
281332

282333
/** A lazy iterable */
@@ -322,6 +373,8 @@ object CollectionStrawMan4 {
322373
}
323374
case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
324375
val left, right = Partitioned(this)
376+
// `distribute` makes up for the lack of generic push-based functionality.
377+
// It forces both halves of the partition with a given builder.
325378
def distribute(bf: => Builder[A, Iterable[A]]) = {
326379
val lb, rb = bf
327380
val it = underlying.iterator

0 commit comments

Comments
 (0)