Skip to content

Fix strawmans #1219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 20, 2016
18 changes: 17 additions & 1 deletion src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,21 @@ object desugar {
// prefixed by type or val). `tparams` and `vparamss` are the type parameters that
// go in `constr`, the constructor after desugaring.

/** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */
def isAnyVal(tree: Tree): Boolean = tree match {
case Ident(tpnme.AnyVal) => true
case Select(qual, tpnme.AnyVal) => isScala(qual)
case _ => false
}
def isScala(tree: Tree): Boolean = tree match {
case Ident(nme.scala_) => true
case Select(Ident(nme.ROOTPKG), nme.scala_) => true
case _ => false
}

val isCaseClass = mods.is(Case) && !mods.is(Module)
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.

val constrTparams = constr1.tparams map toDefParam
val constrVparamss =
Expand Down Expand Up @@ -398,7 +412,9 @@ object desugar {
companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
}
else if (defaultGetters.nonEmpty)
companionDefs(anyRef, defaultGetters)
companionDefs(anyRef, defaultGetters)
else if (isValueClass)
companionDefs(anyRef, Nil)
else Nil


Expand Down
18 changes: 16 additions & 2 deletions src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,12 @@ object NameOps {
name.drop(tpnme.hkArgPrefixLength).toString.toInt

def isLambdaTraitName(implicit ctx: Context): Boolean =
name.startsWith(tpnme.hkLambdaPrefix)
name.isTypeName && name.startsWith(tpnme.hkLambdaPrefix)

def lambdaTraitVariances(implicit ctx: Context): List[Int] = {
val vs = name.drop(tpnme.hkLambdaPrefix.length)
vs.toList.map(c => tpnme.varianceSuffixes.indexOf(c) - 1)
}

/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
Expand Down Expand Up @@ -179,7 +184,13 @@ object NameOps {
* an encoded name, e.g. super$$plus$eq. See #765.
*/
def unexpandedName: N = {
val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
var idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)

// Hack to make super accessors from traits work. They would otherwise fail because of #765
// TODO: drop this once we have more robust name handling
if (name.slice(idx - FalseSuperLength, idx) == FalseSuper)
idx -= FalseSuper.length

if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N]
}

Expand Down Expand Up @@ -431,4 +442,7 @@ object NameOps {
name.dropRight(nme.LAZY_LOCAL.length)
}
}

private final val FalseSuper = "$$super".toTermName
private val FalseSuperLength = FalseSuper.length
}
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object Names {
*/
abstract class Name extends DotClass
with PreName
with Seq[Char]
with collection.immutable.Seq[Char]
with IndexedSeqOptimized[Char, Name] {

/** A type for names of the same kind as this name */
Expand Down
12 changes: 10 additions & 2 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,14 @@ class TypeApplications(val self: Type) extends AnyVal {
false
}

/** Dealias type if it can be done without forcing anything */
def safeDealias(implicit ctx: Context): Type = self match {
case self: TypeRef if self.denot.exists && self.symbol.isAliasType =>
self.info.bounds.hi.stripTypeVar.safeDealias
case _ =>
self
}

/** Replace references to type parameters with references to hk arguments `this.$hk_i`
* Care is needed not to cause cyclic reference errors, hence `SafeSubstMap`.
*/
Expand Down Expand Up @@ -546,8 +554,8 @@ class TypeApplications(val self: Type) extends AnyVal {
substHkArgs(body)
case self: PolyType =>
self.instantiate(args)
case _ =>
appliedTo(args, typeParams)
case self1 =>
self1.safeDealias.appliedTo(args, typeParams)
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,9 @@ object Types {
case pre: RefinedType =>
object instantiate extends TypeMap {
var isSafe = true
def apply(tp: Type): Type = tp match {
def apply(tp: Type): Type =
if (!isSafe) tp
else tp match {
case TypeRef(RefinedThis(`pre`), name) if name.isHkArgName =>
member(name).info match {
case TypeAlias(alias) => alias
Expand Down Expand Up @@ -2061,7 +2063,7 @@ object Types {
false
}
override def computeHash = doHash(refinedName, refinedInfo, parent)
override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)"
override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)"
}

class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) {
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
readPackageRef().termRef
case TYPEREF =>
val name = readName().toTypeName
if (name.isLambdaTraitName) // Make sure curresponding lambda trait exists
defn.LambdaTrait(name.lambdaTraitVariances)
TypeRef(readType(), name)
case TERMREF =>
readNameSplitSig() match {
Expand Down
6 changes: 1 addition & 5 deletions src/dotty/tools/dotc/transform/ExtensionMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import SymUtils._
* This is different from the implementation of value classes in Scala 2
* (see SIP-15) which uses `asInstanceOf` which does not typecheck.
*/
class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization with NeedsCompanions { thisTransformer =>
class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer =>

import tpd._
import ExtensionMethods._
Expand All @@ -45,10 +45,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful

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

def isCompanionNeeded(cls: ClassSymbol)(implicit ctx: Context): Boolean = {
isDerivedValueClass(cls)
}

override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass =>
moduleClassSym.linkedClass match {
Expand Down
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/transform/FirstTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
this
}

def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = {
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp/*{
tp match {
//create companions for value classes that are not from currently compiled source file
case tp@ClassInfo(_, cls, _, decls, _)
Expand All @@ -59,7 +59,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
case _ => tp
}
}

*/
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case Select(qual, _) if tree.symbol.exists =>
Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/transform/SyntheticMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DenotTransformers._
import ast.Trees._
import ast.untpd
import Decorators._
import NameOps._
import ValueClasses.isDerivedValueClass
import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
Expand Down Expand Up @@ -79,14 +80,17 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
def forwardToRuntime(vrefss: List[List[Tree]]): Tree =
ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head)

def ownName(vrefss: List[List[Tree]]): Tree =
Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString))

def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match {
case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody
case nme.hashCode_ => vrefss => caseHashCodeBody
case nme.toString_ => forwardToRuntime
case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime
case nme.equals_ => vrefss => equalsBody(vrefss.head.head)
case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
case nme.productArity => vrefss => Literal(Constant(accessors.length))
case nme.productPrefix => vrefss => Literal(Constant(clazz.name.decode.toString))
case nme.productPrefix => ownName
}
ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}")
DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic)))
Expand Down
111 changes: 82 additions & 29 deletions src/strawman/collections/CollectionStrawMan4.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package strawman.collections

import Predef.{augmentString => _, wrapString => _, _}
import scala.reflect.ClassTag
import annotation.unchecked.uncheckedVariance
import annotation.tailrec

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

/** Base trait for generic collections */
trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
def iterator: Iterator[A]
def view: View[A] = View.fromIterator(iterator)
def knownLength: Int = -1
}

/** Base trait for instances that can construct a collection from an iterator */
/** Base trait for instances that can construct a collection from an iterable */
trait FromIterable[+C[X] <: Iterable[X]] {
def fromIterable[B](v: Iterable[B]): C[B]
}
Expand All @@ -38,16 +33,27 @@ object CollectionStrawMan4 {
def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
}

/** Base trait for generic collections */
trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
def view: View[A] = View.fromIterator(iterator) // view is overridden, cannot be defined in ops
def knownLength: Int = -1
}

/** Base trait for sequence collections */
trait Seq[+A] extends Iterable[A] with FromIterable[Seq] {
def apply(i: Int): A
def length: Int
}

/** Base trait for collection builders */
trait Builder[-A, +To] {
def +=(x: A): this.type
def ++=(xs: IterableOnce[A]): Unit = xs.iterator.foreach(+=)
def result: To

def ++=(xs: IterableOnce[A]): this.type = {
xs.iterator.foreach(+=)
this
}
}

/* ------------ Operations ----------------------------------- */
Expand Down Expand Up @@ -134,17 +140,18 @@ object CollectionStrawMan4 {
require(!isEmpty)
if (i == 0) head else tail.apply(i - 1)
}
def :::[B >: A](prefix: List[B]): List[B] =
if (prefix.isEmpty) this
else Cons(prefix.head, prefix.tail ::: this)
def length: Int =
if (isEmpty) 0 else 1 + tail.length
def ++:[B >: A](prefix: List[B]): List[B] =
if (prefix.isEmpty) this
else Cons(prefix.head, prefix.tail ++: this)
}

case class Cons[+A](x: A, xs: List[A]) extends List[A] {
case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
extends List[A] {
def isEmpty = false
def head = x
def tail = xs
def tail = next
}

case object Nil extends List[Nothing] {
Expand All @@ -157,20 +164,64 @@ object CollectionStrawMan4 {
def fromIterator[B](it: Iterator[B]): List[B] =
if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil
def fromIterable[B](c: Iterable[B]): List[B] = c match {
case View.Concat(xs, ys: Iterable[B]) =>
fromIterable(xs) ::: fromIterable(ys)
case View.Concat(xs, ys: List[B]) =>
fromIterable(xs) ++: ys
case View.Drop(xs: List[B], n) =>
var i = 0
var ys = xs
while (i < n && !xs.isEmpty) {
ys = ys.tail
i += 1
}
ys
@tailrec def loop(xs: List[B], n: Int): List[B] =
if (n > 0) loop(xs.tail, n - 1) else xs
loop(xs, n)
case c: List[B] => c
case _ => fromIterator(c.iterator)
}
}

/** Concrete collection type: ListBuffer */
class ListBuffer[A] extends Seq[A] with FromIterable[ListBuffer] with Builder[A, List[A]] {
private var first, last: List[A] = Nil
private var aliased = false
def iterator = first.iterator
def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
def apply(i: Int) = first.apply(i)
def length = first.length

private def copyElems(): Unit = {
val buf = ListBuffer.fromIterable(result)
first = buf.first
last = buf.last
aliased = false
}
def result = {
aliased = true
first
}
def +=(elem: A) = {
if (aliased) copyElems()
val last1 = Cons(elem, Nil)
last match {
case last: Cons[A] => last.next = last1
case _ => first = last1
}
last = last1
this
}
override def toString: String =
if (first.isEmpty) "ListBuffer()"
else {
val b = new StringBuilder("ListBuffer(").append(first.head)
first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
}
}

object ListBuffer extends IterableFactory[ListBuffer] {
def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = coll match {
case pd @ View.Partitioned(partition: View.Partition[B]) =>
partition.distribute(new ListBuffer[B]())
pd.forced.get.asInstanceOf[ListBuffer[B]]
case _ =>
new ListBuffer[B] ++= coll
}
}

/** Concrete collection type: ArrayBuffer */
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
extends Seq[A] with FromIterable[ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
Expand Down Expand Up @@ -234,12 +285,6 @@ object CollectionStrawMan4 {
def apply(n: Int) = elems(start + n).asInstanceOf[A]
}

case class StringView(s: String) extends RandomAccessView[Char] {
val start = 0
val end = s.length
def apply(n: Int) = s.charAt(n)
}

/** Concrete collection type: String */
implicit class StringOps(val s: String) extends AnyVal with Ops[Char] {
def iterator: Iterator[Char] = new StringView(s).iterator
Expand Down Expand Up @@ -277,6 +322,12 @@ object CollectionStrawMan4 {
def ++(xs: String): String = s + xs
}

case class StringView(s: String) extends RandomAccessView[Char] {
val start = 0
val end = s.length
def apply(n: Int) = s.charAt(n)
}

/* ------------ Views --------------------------------------- */

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