aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/dotc4
-rw-r--r--project/plugins.sbt2
-rw-r--r--src/dotty/tools/backend/sjs/JSCodeGen.scala1376
-rw-r--r--src/dotty/tools/backend/sjs/JSDefinitions.scala24
-rw-r--r--src/dotty/tools/backend/sjs/JSEncoding.scala43
-rw-r--r--src/dotty/tools/backend/sjs/JSInterop.scala110
-rw-r--r--src/dotty/tools/backend/sjs/JSPrimitives.scala17
-rw-r--r--src/dotty/tools/dotc/Compiler.scala6
-rw-r--r--src/dotty/tools/dotc/Run.scala2
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala40
-rw-r--r--src/dotty/tools/dotc/ast/NavigateAST.scala83
-rw-r--r--src/dotty/tools/dotc/ast/Positioned.scala78
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala40
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala24
-rw-r--r--src/dotty/tools/dotc/ast/untpd.scala4
-rw-r--r--src/dotty/tools/dotc/config/JavaPlatform.scala9
-rw-r--r--src/dotty/tools/dotc/config/Platform.scala3
-rw-r--r--src/dotty/tools/dotc/config/SJSPlatform.scala5
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala5
-rw-r--r--src/dotty/tools/dotc/config/Settings.scala9
-rw-r--r--src/dotty/tools/dotc/core/Phases.scala4
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala2
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala2
-rw-r--r--src/dotty/tools/dotc/core/Types.scala2
-rw-r--r--src/dotty/tools/dotc/core/classfile/ClassfileParser.scala9
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreePickler.scala4
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala5
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala6
-rw-r--r--src/dotty/tools/dotc/parsing/JavaParsers.scala4
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala61
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala4
-rw-r--r--src/dotty/tools/dotc/rewrite/Rewrites.scala92
-rw-r--r--src/dotty/tools/dotc/transform/ElimRepeated.scala4
-rw-r--r--src/dotty/tools/dotc/transform/ExpandSAMs.scala12
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala7
-rw-r--r--src/dotty/tools/dotc/transform/Getters.scala4
-rw-r--r--src/dotty/tools/dotc/transform/LazyVals.scala19
-rw-r--r--src/dotty/tools/dotc/transform/SeqLiterals.scala4
-rw-r--r--src/dotty/tools/dotc/transform/TreeTransform.scala3
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala75
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ReTyper.scala2
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala14
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala53
-rw-r--r--src/dotty/tools/dotc/typer/VarianceChecker.scala15
-rw-r--r--src/strawman/collections/CollectionStrawMan4.scala486
-rw-r--r--test/dotc/tests.scala2
-rw-r--r--test/test/CompilerTest.scala18
-rw-r--r--test/test/DeSugarTest.scala4
-rw-r--r--tests/neg/classOf.scala11
-rw-r--r--tests/neg/i1059.scala10
-rw-r--r--tests/neg/overloaded.scala17
-rw-r--r--tests/neg/tate.scala11
-rw-r--r--tests/pending/neg/tate.scala11
-rw-r--r--tests/pending/pos/lazyvals.scala18
-rw-r--r--tests/pickling/hk.scala2
-rw-r--r--tests/pos-scala2/i1059.scala10
-rw-r--r--tests/pos-scala2/rewrites.scala36
-rw-r--r--tests/pos/hk.scala2
-rw-r--r--tests/pos/i1174.scala19
-rw-r--r--tests/pos/overloaded.scala26
-rw-r--r--tests/run/CollectionTests.scala8
62 files changed, 2714 insertions, 270 deletions
diff --git a/bin/dotc b/bin/dotc
index 993f58df8..5798e0523 100755
--- a/bin/dotc
+++ b/bin/dotc
@@ -245,7 +245,7 @@ main_class=$CompilerMain
while [[ $# -gt 0 ]]; do
case "$1" in
--) shift; for arg; do addResidual "$arg"; done; set -- ;;
- -h|-help) usage; exit 1 ;;
+ -h|-help) help=true && shift ;;
-bootstrapped) bootstrapped=true && shift ;;
-v|-verbose) verbose=true && shift ;;
-debug) debug=true && shift ;;
@@ -259,7 +259,7 @@ case "$1" in
-run) main_class=$ReplMain && shift ;;
-fsc) main_class=$FscMain && shift ;;
-bootcp) bootcp=true && shift ;;
- -no-bootcp) unset bootcp && shift ;;
+ -nobootcp) unset bootcp && shift ;;
-colors) colors=true && shift ;;
-no-colors) unset colors && shift ;;
-jrebel) jrebel=true && shift ;;
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 0c8b7737e..57bd46581 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -7,4 +7,4 @@ addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")
-addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7")
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.8")
diff --git a/src/dotty/tools/backend/sjs/JSCodeGen.scala b/src/dotty/tools/backend/sjs/JSCodeGen.scala
index be4e56375..ec75a1c4d 100644
--- a/src/dotty/tools/backend/sjs/JSCodeGen.scala
+++ b/src/dotty/tools/backend/sjs/JSCodeGen.scala
@@ -14,23 +14,23 @@ import dotty.tools.dotc.core._
import Periods._
import SymDenotations._
import Contexts._
+import Decorators._
import Flags._
import dotty.tools.dotc.ast.Trees._
import Types._
import Symbols._
import Denotations._
import Phases._
-import dotty.tools.dotc.util.Positions
-import Positions.Position
import StdNames._
import dotty.tools.dotc.transform.Erasure
import org.scalajs.core.ir
-import org.scalajs.core.ir.{ClassKind, Trees => js, Types => jstpe}
+import org.scalajs.core.ir.{ClassKind, Position, Trees => js, Types => jstpe}
import js.OptimizerHints
import JSEncoding._
+import JSInterop._
import ScopedVar.withScopedVars
/** Main codegen for Scala.js IR.
@@ -76,6 +76,14 @@ class JSCodeGen()(implicit ctx: Context) {
private def currentClassType = encodeClassType(currentClassSym)
+ /** Returns a new fresh local identifier. */
+ private def freshLocalIdent()(implicit pos: Position): js.Ident =
+ localNames.get.freshLocalIdent()
+
+ /** Returns a new fresh local identifier. */
+ private def freshLocalIdent(base: String)(implicit pos: Position): js.Ident =
+ localNames.get.freshLocalIdent(base)
+
// Compilation unit --------------------------------------------------------
def run(): Unit = {
@@ -129,7 +137,7 @@ class JSCodeGen()(implicit ctx: Context) {
withScopedVars(
currentClassSym := sym
) {
- val tree = if (isRawJSType(sym)) {
+ val tree = if (isJSType(sym)) {
/*assert(!isRawJSFunctionDef(sym),
s"Raw JS function def should have been recorded: $cd")*/
if (!sym.is(Trait) && isScalaJSDefinedJSClass(sym))
@@ -327,7 +335,23 @@ class JSCodeGen()(implicit ctx: Context) {
/** Gen the IR ClassDef for a raw JS class or trait.
*/
private def genRawJSClassData(td: TypeDef): js.ClassDef = {
- ???
+ val sym = td.symbol.asClass
+ implicit val pos: Position = sym.pos
+
+ val classIdent = encodeClassFullNameIdent(sym)
+ val superClass =
+ if (sym.is(Trait)) None
+ else Some(encodeClassFullNameIdent(sym.superClass))
+ val jsName =
+ if (sym.is(Trait) || sym.is(ModuleClass)) None
+ else Some(fullJSNameOf(sym))
+
+ js.ClassDef(classIdent, ClassKind.RawJSType,
+ superClass,
+ genClassInterfaces(sym),
+ jsName,
+ Nil)(
+ OptimizerHints.empty)
}
/** Gen the IR ClassDef for an interface definition.
@@ -533,21 +557,7 @@ class JSCodeGen()(implicit ctx: Context) {
}
}
- /* Work around https://github.com/scala-js/scala-js/issues/2259
- * TODO Remove this when we upgrade to Scala.js 0.6.8.
- */
- val methodDef1 = if (!sym.owner.is(Trait)) {
- methodDef
- } else {
- val workaroundBody = js.Block(
- js.Apply(js.ClassOf(jstpe.ClassType(encodeClassFullName(sym.owner))),
- js.Ident("isPrimitive__Z"), Nil)(jstpe.BooleanType),
- methodDef.body)
- methodDef.copy(body = workaroundBody)(
- methodDef.optimizerHints, methodDef.hash)
- }
-
- Some(methodDef1)
+ Some(methodDef)
}
}
}
@@ -611,7 +621,7 @@ class JSCodeGen()(implicit ctx: Context) {
/* Any JavaScript expression is also a statement, but at least we get rid
* of some pure expressions that come from our own codegen.
*/
- implicit val pos: ir.Position = tree.pos
+ implicit val pos: Position = tree.pos
tree match {
case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr))
case _:js.Literal | js.This() => js.Skip()
@@ -682,22 +692,12 @@ class JSCodeGen()(implicit ctx: Context) {
/*case t: Try =>
genTry(t, isStat)*/
- /*case Throw(expr) =>
- val ex = genExpr(expr)
- js.Throw {
- if (isMaybeJavaScriptException(expr.tpe)) {
- genApplyMethod(
- genLoadModule(RuntimePackageModule),
- Runtime_unwrapJavaScriptException,
- List(ex))
- } else {
- ex
- }
- }*/
-
case app: Apply =>
genApply(app, isStat)
+ case app: TypeApply =>
+ genTypeApply(app)
+
/*case app: ApplyDynamic =>
genApplyDynamic(app)*/
@@ -831,23 +831,22 @@ class JSCodeGen()(implicit ctx: Context) {
}
/** Array constructor */
- /*case av: ArrayValue =>
- genArrayValue(av)
+ case javaSeqLiteral: JavaSeqLiteral =>
+ genJavaSeqLiteral(javaSeqLiteral)
/** A Match reaching the backend is supposed to be optimized as a switch */
- case mtch: Match =>
- genMatch(mtch, isStat)
+ /*case mtch: Match =>
+ genMatch(mtch, isStat)*/
- /** Anonymous function (only with -Ydelambdafy:method) */
- case fun: Function =>
- genAnonFunction(fun)
+ case tree: Closure =>
+ genClosure(tree)
- case EmptyTree =>
+ /*case EmptyTree =>
js.Skip()*/
case _ =>
throw new FatalError("Unexpected tree in genExpr: " +
- tree + "/" + tree.getClass + " at: " + tree.pos)
+ tree + "/" + tree.getClass + " at: " + (tree.pos: Position))
}
} // end of genStatOrExpr()
@@ -866,6 +865,18 @@ class JSCodeGen()(implicit ctx: Context) {
}
}
+ private def qualifierOf(fun: Tree): Tree = fun match {
+ case fun: Ident =>
+ fun.tpe match {
+ case TermRef(prefix: TermRef, _) => tpd.ref(prefix)
+ case TermRef(prefix: ThisType, _) => tpd.This(prefix.cls)
+ }
+ case Select(qualifier, _) =>
+ qualifier
+ case TypeApply(fun, _) =>
+ qualifierOf(fun)
+ }
+
/** Gen JS this of the current class.
* Normally encoded straightforwardly as a JS this.
* But must be replaced by the `thisLocalVarIdent` local variable if there
@@ -900,35 +911,22 @@ class JSCodeGen()(implicit ctx: Context) {
case fun => fun
}
- def isRawJSDefaultParam: Boolean = {
- false /*
- if (isCtorDefaultParam(sym)) {
- isRawJSCtorDefaultParam(sym)
- } else {
- sym.hasFlag(reflect.internal.Flags.DEFAULTPARAM) &&
- isRawJSType(sym.owner.tpe)
- }*/
- }
-
fun match {
- /*case _: TypeApply =>
- genApplyTypeApply(tree)*/
-
- /*case _ if isRawJSDefaultParam =>
- js.UndefinedParam()(toIRType(sym.tpe.resultType))*/
+ case _ if isJSDefaultParam(sym) =>
+ js.UndefinedParam()(toIRType(sym.info.finalResultType))
case Select(Super(_, _), _) =>
genSuperCall(tree, isStat)
- /*case Select(New(_), nme.CONSTRUCTOR) =>
- genApplyNew(tree)*/
+ case Select(New(_), nme.CONSTRUCTOR) =>
+ genApplyNew(tree)
case _ =>
/*if (sym.isLabel) {
genLabelApply(tree)
- } else if (primitives.isPrimitive(tree)) {
+ } else*/ if (primitives.isPrimitive(tree)) {
genPrimitiveOp(tree, isStat)
- } else*/ if (Erasure.Boxing.isBox(sym)) {
+ } else if (Erasure.Boxing.isBox(sym)) {
// Box a primitive value (cannot be Unit)
val arg = args.head
makePrimitiveBox(genExpr(arg), arg.tpe)
@@ -979,6 +977,607 @@ class JSCodeGen()(implicit ctx: Context) {
}
}
+ /** Gen JS code for a constructor call (new).
+ * Further refined into:
+ * * new String(...)
+ * * new of a hijacked boxed class
+ * * new of an anonymous function class that was recorded as JS function
+ * * new of a raw JS class
+ * * new Array
+ * * regular new
+ */
+ private def genApplyNew(tree: Apply): js.Tree = {
+ implicit val pos: Position = tree.pos
+
+ val Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) = tree
+ val ctor = fun.symbol
+ val tpe = tpt.tpe
+
+ assert(ctor.isClassConstructor,
+ "'new' call to non-constructor: " + ctor.name)
+
+ if (tpe.isRef(defn.StringClass)) {
+ genNewString(ctor, genActualArgs(ctor, args))
+ } else /*if (isHijackedBoxedClass(tpe.typeSymbol)) {
+ genNewHijackedBoxedClass(tpe.typeSymbol, ctor, args map genExpr)
+ } else if (translatedAnonFunctions contains tpe.typeSymbol) {
+ val functionMaker = translatedAnonFunctions(tpe.typeSymbol)
+ functionMaker(args map genExpr)
+ } else*/ if (isJSType(tpe.widenDealias.typeSymbol)) {
+ val clsSym = tpe.widenDealias.typeSymbol
+ if (clsSym == jsdefn.JSObjectClass && args.isEmpty) js.JSObjectConstr(Nil)
+ else if (clsSym == jsdefn.JSArrayClass && args.isEmpty) js.JSArrayConstr(Nil)
+ else js.JSNew(genLoadJSConstructor(clsSym), genActualJSArgs(ctor, args))
+ } else {
+ toIRType(tpe) match {
+ case cls: jstpe.ClassType =>
+ js.New(cls, encodeMethodSym(ctor), genActualArgs(ctor, args))
+
+ case other =>
+ throw new FatalError(s"Non ClassType cannot be instantiated: $other")
+ }
+ }
+ }
+
+ /** Gen JS code for a primitive method call. */
+ private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ implicit val pos: Position = tree.pos
+
+ val Apply(fun, args) = tree
+ val receiver = qualifierOf(fun)
+
+ val code = primitives.getPrimitive(tree, receiver.tpe)
+
+ if (isArithmeticOp(code) || isLogicalOp(code) || isComparisonOp(code))
+ genSimpleOp(tree, receiver :: args, code)
+ else if (code == CONCAT)
+ genStringConcat(tree, receiver, args)
+ else if (code == HASH)
+ genScalaHash(tree, receiver)
+ else if (isArrayNew(code))
+ genArrayNew(tree, code)
+ else if (isArrayOp(code))
+ genArrayOp(tree, code)
+ else if (code == SYNCHRONIZED)
+ genSynchronized(tree, isStat)
+ else if (isCoercion(code))
+ genCoercion(tree, receiver, code)
+ else if (code == JSPrimitives.THROW)
+ genThrow(tree, args)
+ else /*if (primitives.isJSPrimitive(code))
+ genJSPrimitive(tree, receiver, args, code)
+ else*/
+ throw new FatalError(s"Unknown primitive: ${tree.symbol.fullName} at: $pos")
+ }
+
+ /** Gen JS code for a simple operation (arithmetic, logical, or comparison) */
+ private def genSimpleOp(tree: Apply, args: List[Tree], code: Int): js.Tree = {
+ args match {
+ case List(arg) => genSimpleUnaryOp(tree, arg, code)
+ case List(lhs, rhs) => genSimpleBinaryOp(tree, lhs, rhs, code)
+ case _ => throw new FatalError("Incorrect arity for primitive")
+ }
+ }
+
+ /** Gen JS code for a simple unary operation. */
+ private def genSimpleUnaryOp(tree: Apply, arg: Tree, code: Int): js.Tree = {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ implicit val pos: Position = tree.pos
+
+ val genArg = genExpr(arg)
+ val resultIRType = toIRType(tree.tpe)
+
+ (code: @switch) match {
+ case POS =>
+ genArg
+
+ case NEG =>
+ (resultIRType: @unchecked) match {
+ case jstpe.IntType =>
+ js.BinaryOp(js.BinaryOp.Int_-, js.IntLiteral(0), genArg)
+ case jstpe.LongType =>
+ js.BinaryOp(js.BinaryOp.Long_-, js.LongLiteral(0), genArg)
+ case jstpe.FloatType =>
+ js.BinaryOp(js.BinaryOp.Float_-, js.FloatLiteral(0.0f), genArg)
+ case jstpe.DoubleType =>
+ js.BinaryOp(js.BinaryOp.Double_-, js.DoubleLiteral(0), genArg)
+ }
+
+ case NOT =>
+ (resultIRType: @unchecked) match {
+ case jstpe.IntType =>
+ js.BinaryOp(js.BinaryOp.Int_^, js.IntLiteral(-1), genArg)
+ case jstpe.LongType =>
+ js.BinaryOp(js.BinaryOp.Long_^, js.LongLiteral(-1), genArg)
+ }
+
+ case ZNOT =>
+ js.UnaryOp(js.UnaryOp.Boolean_!, genArg)
+
+ case _ =>
+ throw new FatalError("Unknown unary operation code: " + code)
+ }
+ }
+
+ /** Gen JS code for a simple binary operation. */
+ private def genSimpleBinaryOp(tree: Apply, lhs: Tree, rhs: Tree, code: Int): js.Tree = {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+ import js.UnaryOp._
+
+ /* Codes for operation types, in an object so that they can be 'final val'
+ * and be used in switch-matches.
+ */
+ object OpTypes {
+ final val DoubleOp = 1
+ final val FloatOp = 2
+ final val LongOp = 3
+ final val IntOp = 4
+ final val BooleanOp = 5
+ final val AnyOp = 6
+ }
+ import OpTypes._
+
+ implicit val pos: Position = tree.pos
+
+ val lhsIRType = toIRType(lhs.tpe)
+ val rhsIRType = toIRType(rhs.tpe)
+
+ val opType = (lhsIRType, rhsIRType) match {
+ case (jstpe.DoubleType, _) | (_, jstpe.DoubleType) => DoubleOp
+ case (jstpe.FloatType, _) | (_, jstpe.FloatType) => FloatOp
+ case (jstpe.LongType, _) | (_, jstpe.LongType) => LongOp
+ case (jstpe.IntType, _) | (_, jstpe.IntType) => IntOp
+ case (jstpe.BooleanType, jstpe.BooleanType) => BooleanOp
+ case _ => AnyOp
+ }
+
+ if (opType == AnyOp && isUniversalEqualityOp(code)) {
+ genUniversalEqualityOp(lhs, rhs, code)
+ } else if (code == ZOR) {
+ js.If(genExpr(lhs), js.BooleanLiteral(true), genExpr(rhs))(jstpe.BooleanType)
+ } else if (code == ZAND) {
+ js.If(genExpr(lhs), genExpr(rhs), js.BooleanLiteral(false))(jstpe.BooleanType)
+ } else {
+ import js.BinaryOp._
+
+ def coerce(tree: js.Tree, opType: Int): js.Tree = (opType: @switch) match {
+ case DoubleOp =>
+ if (tree.tpe == jstpe.LongType) js.UnaryOp(LongToDouble, tree)
+ else tree
+
+ case FloatOp =>
+ if (tree.tpe == jstpe.FloatType || tree.tpe == jstpe.IntType) tree
+ else js.UnaryOp(DoubleToFloat, coerce(tree, DoubleOp))
+
+ case LongOp =>
+ if (tree.tpe == jstpe.LongType) tree
+ else {
+ assert(tree.tpe == jstpe.IntType)
+ js.UnaryOp(IntToLong, tree)
+ }
+
+ case IntOp =>
+ if (tree.tpe == jstpe.IntType) tree
+ else {
+ assert(tree.tpe == jstpe.LongType)
+ js.UnaryOp(LongToInt, tree)
+ }
+
+ case BooleanOp | AnyOp =>
+ tree
+ }
+
+ val rhsOpType = code match {
+ case LSL | LSR | ASR => IntOp
+ case _ => opType
+ }
+
+ val genLhs = coerce(genExpr(lhs), opType)
+ val genRhs = coerce(genExpr(rhs), rhsOpType)
+
+ val op = (opType: @switch) match {
+ case IntOp =>
+ (code: @switch) match {
+ case ADD => Int_+
+ case SUB => Int_-
+ case MUL => Int_*
+ case DIV => Int_/
+ case MOD => Int_%
+ case OR => Int_|
+ case AND => Int_&
+ case XOR => Int_^
+ case LSL => Int_<<
+ case LSR => Int_>>>
+ case ASR => Int_>>
+
+ case EQ => Num_==
+ case NE => Num_!=
+ case LT => Num_<
+ case LE => Num_<=
+ case GT => Num_>
+ case GE => Num_>=
+ }
+
+ case FloatOp =>
+ (code: @switch) match {
+ case ADD => Float_+
+ case SUB => Float_-
+ case MUL => Float_*
+ case DIV => Float_/
+ case MOD => Float_%
+
+ case EQ => Num_==
+ case NE => Num_!=
+ case LT => Num_<
+ case LE => Num_<=
+ case GT => Num_>
+ case GE => Num_>=
+ }
+
+ case DoubleOp =>
+ (code: @switch) match {
+ case ADD => Double_+
+ case SUB => Double_-
+ case MUL => Double_*
+ case DIV => Double_/
+ case MOD => Double_%
+
+ case EQ => Num_==
+ case NE => Num_!=
+ case LT => Num_<
+ case LE => Num_<=
+ case GT => Num_>
+ case GE => Num_>=
+ }
+
+ case LongOp =>
+ (code: @switch) match {
+ case ADD => Long_+
+ case SUB => Long_-
+ case MUL => Long_*
+ case DIV => Long_/
+ case MOD => Long_%
+ case OR => Long_|
+ case XOR => Long_^
+ case AND => Long_&
+ case LSL => Long_<<
+ case LSR => Long_>>>
+ case ASR => Long_>>
+
+ case EQ => Long_==
+ case NE => Long_!=
+ case LT => Long_<
+ case LE => Long_<=
+ case GT => Long_>
+ case GE => Long_>=
+ }
+
+ case BooleanOp =>
+ (code: @switch) match {
+ case EQ => Boolean_==
+ case NE => Boolean_!=
+ case OR => Boolean_|
+ case AND => Boolean_&
+ case XOR => Boolean_!=
+ }
+
+ case AnyOp =>
+ /* No @switch because some 2.11 version erroneously report a warning
+ * for switches with less than 3 non-default cases.
+ */
+ code match {
+ case ID => ===
+ case NI => !==
+ }
+ }
+
+ js.BinaryOp(op, genLhs, genRhs)
+ }
+ }
+
+ /** Gen JS code for a universal equality test. */
+ private def genUniversalEqualityOp(lhs: Tree, rhs: Tree, code: Int)(
+ implicit pos: Position): js.Tree = {
+
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ val genLhs = genExpr(lhs)
+ val genRhs = genExpr(rhs)
+
+ val bypassEqEq = {
+ // Do not call equals if we have a literal null at either side.
+ genLhs.isInstanceOf[js.Null] ||
+ genRhs.isInstanceOf[js.Null]
+ }
+
+ if (bypassEqEq) {
+ js.BinaryOp(
+ if (code == EQ) js.BinaryOp.=== else js.BinaryOp.!==,
+ genLhs, genRhs)
+ } else {
+ val body = genEqEqPrimitive(lhs.tpe, rhs.tpe, genLhs, genRhs)
+ if (code == EQ) body
+ else js.UnaryOp(js.UnaryOp.Boolean_!, body)
+ }
+ }
+
+ private lazy val externalEqualsNumNum: Symbol =
+ defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum)
+ private lazy val externalEqualsNumChar: Symbol =
+ NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private
+ private lazy val externalEqualsNumObject: Symbol =
+ defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject)
+ private lazy val externalEquals: Symbol =
+ defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol
+
+ /** Gen JS code for a call to Any.== */
+ private def genEqEqPrimitive(ltpe: Type, rtpe: Type, lsrc: js.Tree, rsrc: js.Tree)(
+ implicit pos: Position): js.Tree = {
+ ctx.debuglog(s"$ltpe == $rtpe")
+ val lsym = ltpe.widenDealias.typeSymbol.asClass
+ val rsym = rtpe.widenDealias.typeSymbol.asClass
+
+ /* True if the equality comparison is between values that require the
+ * use of the rich equality comparator
+ * (scala.runtime.BoxesRunTime.equals).
+ * This is the case when either side of the comparison might have a
+ * run-time type subtype of java.lang.Number or java.lang.Character,
+ * **which includes when either is a JS type**.
+ * When it is statically known that both sides are equal and subtypes of
+ * Number or Character, not using the rich equality is possible (their
+ * own equals method will do ok.)
+ */
+ val mustUseAnyComparator: Boolean = {
+ isJSType(lsym) || isJSType(rsym) || {
+ val p = ctx.platform
+ val areSameFinals = lsym.is(Final) && rsym.is(Final) && (ltpe =:= rtpe)
+ !areSameFinals && p.isMaybeBoxed(lsym) && p.isMaybeBoxed(rsym)
+ }
+ }
+
+ if (mustUseAnyComparator) {
+ val equalsMethod: Symbol = {
+ // scalastyle:off line.size.limit
+ val ptfm = ctx.platform
+ if (lsym.derivesFrom(defn.BoxedNumberClass)) {
+ if (rsym.derivesFrom(defn.BoxedNumberClass)) externalEqualsNumNum
+ else if (rsym.derivesFrom(defn.BoxedCharClass)) externalEqualsNumObject // will be externalEqualsNumChar in 2.12, SI-9030
+ else externalEqualsNumObject
+ } else externalEquals
+ // scalastyle:on line.size.limit
+ }
+ genModuleApplyMethod(equalsMethod, List(lsrc, rsrc))
+ } else {
+ // if (lsrc eq null) rsrc eq null else lsrc.equals(rsrc)
+ if (lsym == defn.StringClass) {
+ // String.equals(that) === (this eq that)
+ js.BinaryOp(js.BinaryOp.===, lsrc, rsrc)
+ } else {
+ /* This requires to evaluate both operands in local values first.
+ * The optimizer will eliminate them if possible.
+ */
+ val ltemp = js.VarDef(freshLocalIdent(), lsrc.tpe, mutable = false, lsrc)
+ val rtemp = js.VarDef(freshLocalIdent(), rsrc.tpe, mutable = false, rsrc)
+ js.Block(
+ ltemp,
+ rtemp,
+ js.If(js.BinaryOp(js.BinaryOp.===, ltemp.ref, js.Null()),
+ js.BinaryOp(js.BinaryOp.===, rtemp.ref, js.Null()),
+ genApplyMethod(ltemp.ref, defn.Any_equals, List(rtemp.ref)))(
+ jstpe.BooleanType))
+ }
+ }
+ }
+
+ /** Gen JS code for string concatenation.
+ */
+ private def genStringConcat(tree: Apply, receiver: Tree,
+ args: List[Tree]): js.Tree = {
+ implicit val pos: Position = tree.pos
+
+ val arg = args.head
+
+ /* Primitive number types such as scala.Int have a
+ * def +(s: String): String
+ * method, which is why we have to box the lhs sometimes.
+ * Otherwise, both lhs and rhs are already reference types (Any or String)
+ * so boxing is not necessary (in particular, rhs is never a primitive).
+ */
+ assert(!isPrimitiveValueType(receiver.tpe) || arg.tpe.isRef(defn.StringClass))
+ assert(!isPrimitiveValueType(arg.tpe))
+
+ val genLhs = {
+ val genLhs0 = genExpr(receiver)
+ // Box the receiver if it is a primitive value
+ if (!isPrimitiveValueType(receiver.tpe)) genLhs0
+ else makePrimitiveBox(genLhs0, receiver.tpe)
+ }
+
+ val genRhs = genExpr(arg)
+
+ js.BinaryOp(js.BinaryOp.String_+, genLhs, genRhs)
+ }
+
+ /** Gen JS code for a call to Any.## */
+ private def genScalaHash(tree: Apply, receiver: Tree): js.Tree = {
+ implicit val pos: Position = tree.pos
+
+ genModuleApplyMethod(defn.ScalaRuntimeModule.requiredMethod(nme.hash_),
+ List(genExpr(receiver)))
+ }
+
+ /** Gen JS code for a new array operation. */
+ private def genArrayNew(tree: Tree, code: Int): js.Tree = {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ implicit val pos: Position = tree.pos
+
+ val Apply(fun, args) = tree
+ val genLength = genExpr(args.head)
+
+ toIRType(tree.tpe) match {
+ case arrayType: jstpe.ArrayType =>
+ js.NewArray(arrayType, List(genLength))
+
+ case irTpe =>
+ throw new FatalError(s"ArrayNew $tree must have an array type but was $irTpe")
+ }
+ }
+
+ /** Gen JS code for an array operation (get, set or length) */
+ private def genArrayOp(tree: Tree, code: Int): js.Tree = {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ implicit val pos: Position = tree.pos
+
+ val Apply(fun, args) = tree
+ val arrayObj = qualifierOf(fun)
+
+ val genArray = genExpr(arrayObj)
+ val genArgs = args.map(genExpr)
+
+ def elementType: Type = arrayObj.tpe.widenDealias match {
+ case defn.ArrayOf(el) => el
+ case JavaArrayType(el) => el
+ case tpe =>
+ ctx.error(s"expected Array $tpe")
+ ErrorType
+ }
+
+ def genSelect(): js.Tree =
+ js.ArraySelect(genArray, genArgs(0))(toIRType(elementType))
+
+ if (isArrayGet(code)) {
+ // get an item of the array
+ assert(args.length == 1,
+ s"Array get requires 1 argument, found ${args.length} in $tree")
+ genSelect()
+ } else if (isArraySet(code)) {
+ // set an item of the array
+ assert(args.length == 2,
+ s"Array set requires 2 arguments, found ${args.length} in $tree")
+ js.Assign(genSelect(), genArgs(1))
+ } else {
+ // length of the array
+ js.ArrayLength(genArray)
+ }
+ }
+
+ /** Gen JS code for a call to AnyRef.synchronized */
+ private def genSynchronized(tree: Apply, isStat: Boolean): js.Tree = {
+ /* JavaScript is single-threaded, so we can drop the
+ * synchronization altogether.
+ */
+ val Apply(fun, List(arg)) = tree
+ val receiver = qualifierOf(fun)
+
+ val genReceiver = genExpr(receiver)
+ val genArg = genStatOrExpr(arg, isStat)
+
+ genReceiver match {
+ case js.This() =>
+ // common case for which there is no side-effect nor NPE
+ genArg
+ case _ =>
+ implicit val pos: Position = tree.pos
+ /* TODO Check for a null receiver?
+ * In theory, it's UB, but that decision should be left for link time.
+ */
+ js.Block(genReceiver, genArg)
+ }
+ }
+
+ /** Gen JS code for a coercion */
+ private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ implicit val pos: Position = tree.pos
+
+ val source = genExpr(receiver)
+
+ def source2int = (code: @switch) match {
+ case F2C | D2C | F2B | D2B | F2S | D2S | F2I | D2I =>
+ js.UnaryOp(js.UnaryOp.DoubleToInt, source)
+ case L2C | L2B | L2S | L2I =>
+ js.UnaryOp(js.UnaryOp.LongToInt, source)
+ case _ =>
+ source
+ }
+
+ (code: @switch) match {
+ // To Char, need to crop at unsigned 16-bit
+ case B2C | S2C | I2C | L2C | F2C | D2C =>
+ js.BinaryOp(js.BinaryOp.Int_&, source2int, js.IntLiteral(0xffff))
+
+ // To Byte, need to crop at signed 8-bit
+ case C2B | S2B | I2B | L2B | F2B | D2B =>
+ // note: & 0xff would not work because of negative values
+ js.BinaryOp(js.BinaryOp.Int_>>,
+ js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(24)),
+ js.IntLiteral(24))
+
+ // To Short, need to crop at signed 16-bit
+ case C2S | I2S | L2S | F2S | D2S =>
+ // note: & 0xffff would not work because of negative values
+ js.BinaryOp(js.BinaryOp.Int_>>,
+ js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(16)),
+ js.IntLiteral(16))
+
+ // To Int, need to crop at signed 32-bit
+ case L2I | F2I | D2I =>
+ source2int
+
+ // Any int to Long
+ case C2L | B2L | S2L | I2L =>
+ js.UnaryOp(js.UnaryOp.IntToLong, source)
+
+ // Any double to Long
+ case F2L | D2L =>
+ js.UnaryOp(js.UnaryOp.DoubleToLong, source)
+
+ // Long to Double
+ case L2D =>
+ js.UnaryOp(js.UnaryOp.LongToDouble, source)
+
+ // Any int, or Double, to Float
+ case C2F | B2F | S2F | I2F | D2F =>
+ js.UnaryOp(js.UnaryOp.DoubleToFloat, source)
+
+ // Long to Float === Long to Double to Float
+ case L2F =>
+ js.UnaryOp(js.UnaryOp.DoubleToFloat,
+ js.UnaryOp(js.UnaryOp.LongToDouble, source))
+
+ // Identities and IR upcasts
+ case C2C | B2B | S2S | I2I | L2L | F2F | D2D |
+ C2I | C2D |
+ B2S | B2I | B2D |
+ S2I | S2D |
+ I2D |
+ F2D =>
+ source
+ }
+ }
+
+ /** Gen a call to the special `throw` method. */
+ private def genThrow(tree: Apply, args: List[Tree]): js.Tree = {
+ implicit val pos: Position = tree.pos
+ val exception = args.head
+ val genException = genExpr(exception)
+ js.Throw {
+ if (exception.tpe.widenDealias.typeSymbol.derivesFrom(jsdefn.JavaScriptExceptionClass)) {
+ genModuleApplyMethod(
+ jsdefn.RuntimePackage_unwrapJavaScriptException,
+ List(genException))
+ } else {
+ genException
+ }
+ }
+ }
+
/** Gen a "normal" apply (to a true method).
*
* But even these are further refined into:
@@ -1004,14 +1603,14 @@ class JSCodeGen()(implicit ctx: Context) {
case _ => false
}
- /*if (sym.owner == defn.StringClass && !isStringMethodFromObject) {
- genStringCall(tree)
- } else if (isRawJSType(sym.owner)) {
- if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym))
- genPrimitiveJSCall(tree, isStat)
- else
- genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))
- } else*/ if (foreignIsImplClass(sym.owner)) {
+ if (sym.owner == defn.StringClass && !isStringMethodFromObject) {
+ genApplyMethodOfString(genExpr(receiver), sym, genActualArgs(sym, args))
+ } else if (isJSType(sym.owner)) {
+ //if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym))
+ genApplyJSMethodGeneric(tree, sym, genExpr(receiver), genActualJSArgs(sym, args), isStat)
+ /*else
+ genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
+ } else if (foreignIsImplClass(sym.owner)) {
genTraitImplApply(sym, args.map(genExpr))
} else if (sym.isClassConstructor) {
// Calls to constructors are always statically linked
@@ -1021,6 +1620,414 @@ class JSCodeGen()(implicit ctx: Context) {
}
}
+ /** Gen JS code for a call to a JS method (of a subclass of `js.Any`).
+ *
+ * Basically it boils down to calling the method as a `JSBracketSelect`,
+ * without name mangling. But other aspects come into play:
+ *
+ * - Operator methods are translated to JS operators (not method calls)
+ * - `apply` is translated as a function call, i.e., `o()` instead of `o.apply()`
+ * - Scala varargs are turned into JS varargs (see `genPrimitiveJSArgs()`)
+ * - Getters and parameterless methods are translated as `JSBracketSelect`
+ * - Setters are translated to `Assign` to `JSBracketSelect`
+ */
+ private def genApplyJSMethodGeneric(tree: Tree, sym: Symbol,
+ receiver: js.Tree, args: List[js.Tree], isStat: Boolean,
+ superIn: Option[Symbol] = None)(
+ implicit pos: Position): js.Tree = {
+
+ implicit val pos: Position = tree.pos
+
+ def noSpread = !args.exists(_.isInstanceOf[js.JSSpread])
+ val argc = args.size // meaningful only for methods that don't have varargs
+
+ def requireNotSuper(): Unit = {
+ if (superIn.isDefined)
+ ctx.error("Illegal super call in Scala.js-defined JS class", tree.pos)
+ }
+
+ def hasExplicitJSEncoding = {
+ sym.hasAnnotation(jsdefn.JSNameAnnot) ||
+ sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) ||
+ sym.hasAnnotation(jsdefn.JSBracketCallAnnot)
+ }
+
+ val boxedResult = sym.name match {
+ case JSUnaryOpMethodName(code) if argc == 0 =>
+ requireNotSuper()
+ js.JSUnaryOp(code, receiver)
+
+ case JSBinaryOpMethodName(code) if argc == 1 =>
+ requireNotSuper()
+ js.JSBinaryOp(code, receiver, args.head)
+
+ case nme.apply if !hasExplicitJSEncoding =>
+ requireNotSuper()
+ if (jsdefn.isJSThisFunctionClass(sym.owner))
+ js.JSBracketMethodApply(receiver, js.StringLiteral("call"), args)
+ else
+ js.JSFunctionApply(receiver, args)
+
+ case _ =>
+ def jsFunName = js.StringLiteral(jsNameOf(sym))
+
+ def genSuperReference(propName: js.Tree): js.Tree = {
+ superIn.fold[js.Tree] {
+ js.JSBracketSelect(receiver, propName)
+ } { superInSym =>
+ js.JSSuperBracketSelect(
+ jstpe.ClassType(encodeClassFullName(superInSym)),
+ receiver, propName)
+ }
+ }
+
+ def genSelectGet(propName: js.Tree): js.Tree =
+ genSuperReference(propName)
+
+ def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree =
+ js.Assign(genSuperReference(propName), value)
+
+ def genCall(methodName: js.Tree, args: List[js.Tree]): js.Tree = {
+ superIn.fold[js.Tree] {
+ js.JSBracketMethodApply(
+ receiver, methodName, args)
+ } { superInSym =>
+ js.JSSuperBracketCall(
+ jstpe.ClassType(encodeClassFullName(superInSym)),
+ receiver, methodName, args)
+ }
+ }
+
+ if (isJSGetter(sym)) {
+ assert(noSpread && argc == 0)
+ genSelectGet(jsFunName)
+ } else if (isJSSetter(sym)) {
+ assert(noSpread && argc == 1)
+ genSelectSet(jsFunName, args.head)
+ } else if (isJSBracketAccess(sym)) {
+ assert(noSpread && (argc == 1 || argc == 2),
+ s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments")
+ args match {
+ case List(keyArg) =>
+ genSelectGet(keyArg)
+ case List(keyArg, valueArg) =>
+ genSelectSet(keyArg, valueArg)
+ }
+ } else if (isJSBracketCall(sym)) {
+ val (methodName, actualArgs) = extractFirstArg(args)
+ genCall(methodName, actualArgs)
+ } else {
+ genCall(jsFunName, args)
+ }
+ }
+
+ if (isStat) {
+ boxedResult
+ } else {
+ val tpe = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx =>
+ sym.info.finalResultType
+ }
+ unbox(boxedResult, tpe)
+ }
+ }
+
+ private object JSUnaryOpMethodName {
+ private val map = Map(
+ nme.UNARY_+ -> js.JSUnaryOp.+,
+ nme.UNARY_- -> js.JSUnaryOp.-,
+ nme.UNARY_~ -> js.JSUnaryOp.~,
+ nme.UNARY_! -> js.JSUnaryOp.!
+ )
+
+ def unapply(name: Names.TermName): Option[js.JSUnaryOp.Code] =
+ map.get(name)
+ }
+
+ private object JSBinaryOpMethodName {
+ private val map = Map(
+ nme.ADD -> js.JSBinaryOp.+,
+ nme.SUB -> js.JSBinaryOp.-,
+ nme.MUL -> js.JSBinaryOp.*,
+ nme.DIV -> js.JSBinaryOp./,
+ nme.MOD -> js.JSBinaryOp.%,
+
+ nme.LSL -> js.JSBinaryOp.<<,
+ nme.ASR -> js.JSBinaryOp.>>,
+ nme.LSR -> js.JSBinaryOp.>>>,
+ nme.OR -> js.JSBinaryOp.|,
+ nme.AND -> js.JSBinaryOp.&,
+ nme.XOR -> js.JSBinaryOp.^,
+
+ nme.LT -> js.JSBinaryOp.<,
+ nme.LE -> js.JSBinaryOp.<=,
+ nme.GT -> js.JSBinaryOp.>,
+ nme.GE -> js.JSBinaryOp.>=,
+
+ nme.ZAND -> js.JSBinaryOp.&&,
+ nme.ZOR -> js.JSBinaryOp.||
+ )
+
+ def unapply(name: Names.TermName): Option[js.JSBinaryOp.Code] =
+ map.get(name)
+ }
+
+ /** Extract the first argument in a list of actual arguments.
+ *
+ * This is nothing else than decomposing into head and tail, except that
+ * we assert that the first element is not a JSSpread.
+ */
+ private def extractFirstArg(args: List[js.Tree]): (js.Tree, List[js.Tree]) = {
+ assert(args.nonEmpty,
+ "Trying to extract the first argument of an empty argument list")
+ val firstArg = args.head
+ assert(!firstArg.isInstanceOf[js.JSSpread],
+ "Trying to extract the first argument of an argument list starting " +
+ "with a Spread argument: " + firstArg)
+ (firstArg, args.tail)
+ }
+
+ /** Gen JS code for a call to a polymorphic method.
+ *
+ * The only methods that reach the back-end as polymorphic are
+ * `isInstanceOf` and `asInstanceOf`.
+ *
+ * (Well, in fact `DottyRunTime.newRefArray` too, but it is handled as a
+ * primitive instead.)
+ */
+ private def genTypeApply(tree: TypeApply): js.Tree = {
+ implicit val pos: Position = tree.pos
+
+ val TypeApply(fun, targs) = tree
+
+ val sym = fun.symbol
+ val receiver = qualifierOf(fun)
+
+ val to = targs.head.tpe
+
+ assert(!isPrimitiveValueType(receiver.tpe),
+ s"Found receiver of type test with primitive type ${receiver.tpe} at $pos")
+ assert(!isPrimitiveValueType(to),
+ s"Found target type of type test with primitive type ${receiver.tpe} at $pos")
+
+ val genReceiver = genExpr(receiver)
+
+ if (sym == defn.Any_asInstanceOf) {
+ genAsInstanceOf(genReceiver, to)
+ } else if (sym == defn.Any_isInstanceOf) {
+ genIsInstanceOf(tree, genReceiver, to)
+ } else {
+ throw new FatalError(
+ s"Unexpected type application $fun with symbol ${sym.fullName}")
+ }
+ }
+
+ /** Gen JS code for a Java Seq literal. */
+ private def genJavaSeqLiteral(tree: JavaSeqLiteral): js.Tree = {
+ implicit val pos: Position = tree.pos
+
+ val genElems = tree.elems.map(genExpr)
+ val arrayType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType]
+ js.ArrayValue(arrayType, genElems)
+ }
+
+ /** Gen JS code for a closure.
+ *
+ * Input: a `Closure` tree of the form
+ * {{{
+ * Closure(env, call, functionalInterface)
+ * }}}
+ * representing the pseudo-syntax
+ * {{{
+ * { (p1, ..., pm) => call(env1, ..., envn, p1, ..., pm) }: functionInterface
+ * }}}
+ * where `envi` are identifiers in the local scope. The qualifier of `call`
+ * is also implicitly captured.
+ *
+ * Output: a `js.Closure` tree of the form
+ * {{{
+ * js.Closure(formalCaptures, formalParams, body, actualCaptures)
+ * }}}
+ * representing the pseudo-syntax
+ * {{{
+ * lambda<formalCapture1 = actualCapture1, ..., formalCaptureN = actualCaptureN>(
+ * formalParam1, ..., formalParamM) = body
+ * }}}
+ * where the `actualCaptures` and `body` are, in general, arbitrary
+ * expressions. But in this case, `actualCaptures` will be identifiers from
+ * `env`, and the `body` will be of the form
+ * {{{
+ * call(formalCapture1.ref, ..., formalCaptureN.ref,
+ * formalParam1.ref, ...formalParamM.ref)
+ * }}}
+ *
+ * When the `js.Closure` node is evaluated, i.e., when the closure value is
+ * created, the expressions of the `actualCaptures` are evaluated, and the
+ * results of those evaluations is "stored" in the environment of the
+ * closure as the corresponding `formalCapture`.
+ *
+ * When we later *call* the closure, the `formalCaptures` already have their
+ * values from the environment, and they are available in the `body`. The
+ * `formalParams` of the created closure receive their values from the
+ * actual arguments at the call-site of the closure, and they are also
+ * available in the `body`.
+ */
+ private def genClosure(tree: Closure): js.Tree = {
+ implicit val pos: Position = tree.pos
+ val Closure(env, call, functionalInterface) = tree
+
+ val envSize = env.size
+
+ val (fun, args) = call match {
+ // case Apply(fun, args) => (fun, args) // Conjectured not to happen
+ case t @ Select(_, _) => (t, Nil)
+ case t @ Ident(_) => (t, Nil)
+ }
+ val sym = fun.symbol
+
+ val qualifier = qualifierOf(fun)
+ val allCaptureValues = qualifier :: env
+
+ val (formalCaptures, actualCaptures) = allCaptureValues.map { value =>
+ implicit val pos: Position = value.pos
+ val formalIdent = value match {
+ case Ident(name) => freshLocalIdent(name.toString)
+ case This(_) => freshLocalIdent("this")
+ case _ => freshLocalIdent()
+ }
+ val formalCapture =
+ js.ParamDef(formalIdent, toIRType(value.tpe), mutable = false, rest = false)
+ val actualCapture = genExpr(value)
+ (formalCapture, actualCapture)
+ }.unzip
+
+ val formalParamNames = sym.info.paramNamess.flatten.drop(envSize)
+ val formalParamTypes = sym.info.paramTypess.flatten.drop(envSize)
+ val (formalParams, actualParams) = formalParamNames.zip(formalParamTypes).map {
+ case (name, tpe) =>
+ val formalParam = js.ParamDef(freshLocalIdent(name.toString),
+ jstpe.AnyType, mutable = false, rest = false)
+ val actualParam = unbox(formalParam.ref, tpe)
+ (formalParam, actualParam)
+ }.unzip
+
+ val genBody = {
+ val thisCaptureRef :: argCaptureRefs = formalCaptures.map(_.ref)
+ val call = genApplyMethod(thisCaptureRef, sym, argCaptureRefs ::: actualParams)
+ box(call, sym.info.finalResultType)
+ }
+
+ val closure = js.Closure(formalCaptures, formalParams, genBody, actualCaptures)
+ ctx.debuglog(closure.toString)
+
+ val funInterfaceSym = functionalInterface.tpe.widenDealias.typeSymbol
+ if (jsdefn.isJSFunctionClass(funInterfaceSym)) {
+ closure
+ } else {
+ assert(!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym),
+ s"Invalid functional interface $funInterfaceSym reached the back-end")
+ val cls = "sjsr_AnonFunction" + formalParams.size
+ val ctor = js.Ident("init___sjs_js_Function" + formalParams.size)
+ js.New(jstpe.ClassType(cls), ctor, List(closure))
+ }
+ }
+
+ /** Boxes a value of the given type before `elimErasedValueType`.
+ *
+ * This should be used when sending values to a JavaScript context, which
+ * is erased/boxed at the IR level, although it is not erased at the
+ * dotty/JVM level.
+ *
+ * @param expr Tree to be boxed if needed.
+ * @param tpeEnteringElimErasedValueType The type of `expr` as it was
+ * entering the `elimErasedValueType` phase.
+ */
+ private def box(expr: js.Tree, tpeEnteringElimErasedValueType: Type)(
+ implicit pos: Position): js.Tree = {
+
+ tpeEnteringElimErasedValueType match {
+ case tpe if isPrimitiveValueType(tpe) =>
+ makePrimitiveBox(expr, tpe)
+
+ /*case tpe: ErasedValueType =>
+ val boxedClass = tpe.valueClazz
+ val ctor = boxedClass.primaryConstructor
+ genNew(boxedClass, ctor, List(expr))*/
+
+ case _ =>
+ expr
+ }
+ }
+
+ /** Unboxes a value typed as Any to the given type before `elimErasedValueType`.
+ *
+ * This should be used when receiving values from a JavaScript context,
+ * which is erased/boxed at the IR level, although it is not erased at the
+ * dotty/JVM level.
+ *
+ * @param expr Tree to be extracted.
+ * @param tpeEnteringElimErasedValueType The type of `expr` as it was
+ * entering the `elimErasedValueType` phase.
+ */
+ private def unbox(expr: js.Tree, tpeEnteringElimErasedValueType: Type)(
+ implicit pos: Position): js.Tree = {
+
+ tpeEnteringElimErasedValueType match {
+ case tpe if isPrimitiveValueType(tpe) =>
+ makePrimitiveUnbox(expr, tpe)
+
+ /*case tpe: ErasedValueType =>
+ val boxedClass = tpe.valueClazz
+ val unboxMethod = boxedClass.derivedValueClassUnbox
+ val content = genApplyMethod(
+ genAsInstanceOf(expr, tpe), unboxMethod, Nil)
+ if (unboxMethod.tpe.resultType <:< tpe.erasedUnderlying)
+ content
+ else
+ fromAny(content, tpe.erasedUnderlying)*/
+
+ case tpe =>
+ genAsInstanceOf(expr, tpe)
+ }
+ }
+
+ /** Gen JS code for an asInstanceOf cast (for reference types only) */
+ private def genAsInstanceOf(value: js.Tree, to: Type)(
+ implicit pos: Position): js.Tree = {
+
+ val sym = to.widenDealias.typeSymbol
+
+ if (sym == defn.ObjectClass || isJSType(sym)) {
+ /* asInstanceOf[Object] always succeeds, and
+ * asInstanceOf to a raw JS type is completely erased.
+ */
+ value
+ } else {
+ js.AsInstanceOf(value, toReferenceType(to))
+ }
+ }
+
+ /** Gen JS code for an isInstanceOf test (for reference types only) */
+ private def genIsInstanceOf(tree: Tree, value: js.Tree, to: Type): js.Tree = {
+ implicit val pos: Position = tree.pos
+ val sym = to.widenDealias.typeSymbol
+
+ if (sym == defn.ObjectClass) {
+ js.BinaryOp(js.BinaryOp.!==, value, js.Null())
+ } else if (isJSType(sym)) {
+ if (sym.is(Trait)) {
+ ctx.error(
+ s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait",
+ tree.pos)
+ js.BooleanLiteral(true)
+ } else {
+ js.Unbox(js.JSBinaryOp(
+ js.JSBinaryOp.instanceof, value, genLoadJSConstructor(sym)), 'Z')
+ }
+ } else {
+ js.IsInstanceOf(value, toReferenceType(to))
+ }
+ }
+
/** Gen a dynamically linked call to a Scala method. */
private def genApplyMethod(receiver: js.Tree,
methodSym: Symbol, arguments: List[js.Tree])(
@@ -1066,6 +2073,33 @@ class JSCodeGen()(implicit ctx: Context) {
genApplyMethod(genLoadModule(methodSym.owner), methodSym, arguments)
}
+ /** Gen JS code for `new java.lang.String(...)`.
+ *
+ * Rewires the instantiation to calling the appropriate overload of
+ * `newString` in the object `scala.scalajs.runtime.RuntimeString`.
+ */
+ private def genNewString(ctor: Symbol, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ js.Apply(
+ genLoadModule(jsdefn.RuntimeStringModuleClass),
+ encodeRTStringCtorSym(ctor), arguments)(
+ jstpe.ClassType(ir.Definitions.StringClass))
+ }
+
+ /** Gen a dynamically linked call to a method of java.lang.String.
+ *
+ * Forwards the call to the module scala.scalajs.runtime.RuntimeString.
+ */
+ private def genApplyMethodOfString(receiver: js.Tree,
+ methodSym: Symbol, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ js.Apply(
+ genLoadModule(jsdefn.RuntimeStringModuleClass),
+ encodeRTStringMethodSym(methodSym),
+ receiver :: arguments)(
+ toIRType(patchedResultType(methodSym)))
+ }
+
/** Gen a boxing operation (tpe is the primitive type) */
private def makePrimitiveBox(expr: js.Tree, tpe: Type)(
implicit pos: Position): js.Tree = {
@@ -1146,7 +2180,156 @@ class JSCodeGen()(implicit ctx: Context) {
}*/
}
- /** Generate loading of a module value
+ /** Gen actual actual arguments to a JS method call.
+ * Returns a list of the transformed arguments.
+ *
+ * - TODO Repeated arguments (varargs) are expanded
+ * - Default arguments are omitted or replaced by undefined
+ * - All arguments are boxed
+ *
+ * Repeated arguments that cannot be expanded at compile time (i.e., if a
+ * Seq is passed to a varargs parameter with the syntax `seq: _*`) will be
+ * wrapped in a [[js.JSSpread]] node to be expanded at runtime.
+ */
+ private def genActualJSArgs(sym: Symbol, args: List[Tree])(
+ implicit pos: Position): List[js.Tree] = {
+
+ def paramNamesAndTypes(implicit ctx: Context): List[(Names.TermName, Type)] =
+ sym.info.paramNamess.flatten.zip(sym.info.paramTypess.flatten)
+
+ val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx =>
+ for ((name, tpe) <- paramNamesAndTypes)
+ yield (name -> tpe.isRepeatedParam)
+ }.toMap
+
+ val paramTypes = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx =>
+ paramNamesAndTypes
+ }.toMap
+
+ var reversedArgs: List[js.Tree] = Nil
+
+ for ((arg, (paramName, paramType)) <- args.zip(paramNamesAndTypes)) {
+ val wasRepeated = wereRepeated.getOrElse(paramName, false)
+ if (wasRepeated) {
+ reversedArgs =
+ genJSRepeatedParam(arg) reverse_::: reversedArgs
+ } else {
+ val unboxedArg = genExpr(arg)
+ val boxedArg = unboxedArg match {
+ case js.UndefinedParam() =>
+ unboxedArg
+ case _ =>
+ val tpe = paramTypes.getOrElse(paramName, paramType)
+ box(unboxedArg, tpe)
+ }
+ reversedArgs ::= boxedArg
+ }
+ }
+
+ /* Remove all consecutive js.UndefinedParam's at the end of the argument
+ * list. No check is performed whether they may be there, since they will
+ * only be placed where default arguments can be anyway.
+ */
+ reversedArgs = reversedArgs.dropWhile(_.isInstanceOf[js.UndefinedParam])
+
+ /* Find remaining js.UndefinedParam and replace by js.Undefined. This can
+ * happen with named arguments or with multiple argument lists.
+ */
+ reversedArgs = reversedArgs map {
+ case js.UndefinedParam() => js.Undefined()
+ case arg => arg
+ }
+
+ reversedArgs.reverse
+ }
+
+ /** Gen JS code for a repeated param of a JS method.
+ *
+ * In this case `arg` has type `Seq[T]` for some `T`, but the result should
+ * be an expanded list of the elements in the sequence. So this method
+ * takes care of the conversion.
+ *
+ * It is specialized for the shapes of tree generated by the desugaring
+ * of repeated params in Scala, so that these are actually expanded at
+ * compile-time.
+ *
+ * Otherwise, it returns a `JSSpread` with the `Seq` converted to a
+ * `js.Array`.
+ */
+ private def genJSRepeatedParam(arg: Tree): List[js.Tree] = {
+ tryGenRepeatedParamAsJSArray(arg, handleNil = true).getOrElse {
+ /* Fall back to calling runtime.genTraversableOnce2jsArray
+ * to perform the conversion to js.Array, then wrap in a Spread
+ * operator.
+ */
+ implicit val pos: Position = arg.pos
+ val jsArrayArg = genModuleApplyMethod(
+ jsdefn.RuntimePackage_genTraversableOnce2jsArray,
+ List(genExpr(arg)))
+ List(js.JSSpread(jsArrayArg))
+ }
+ }
+
+ /** Try and expand an actual argument to a repeated param `(xs: T*)`.
+ *
+ * This method recognizes the shapes of tree generated by the desugaring
+ * of repeated params in Scala, and expands them.
+ * If `arg` does not have the shape of a generated repeated param, this
+ * method returns `None`.
+ */
+ private def tryGenRepeatedParamAsJSArray(arg: Tree,
+ handleNil: Boolean): Option[List[js.Tree]] = {
+ implicit val pos: Position = arg.pos
+
+ // Given a method `def foo(args: T*)`
+ arg match {
+ // foo(arg1, arg2, ..., argN) where N > 0
+ case MaybeAsInstanceOf(WrapArray(MaybeAsInstanceOf(array: JavaSeqLiteral))) =>
+ /* Value classes in arrays are already boxed, so no need to use
+ * the type before erasure.
+ * TODO Is this true in dotty?
+ */
+ Some(array.elems.map(e => box(genExpr(e), e.tpe)))
+
+ // foo()
+ case Ident(_) if handleNil && arg.symbol == defn.NilModule =>
+ Some(Nil)
+
+ // foo(argSeq: _*) - cannot be optimized
+ case _ =>
+ None
+ }
+ }
+
+ private object MaybeAsInstanceOf {
+ def unapply(tree: Tree): Some[Tree] = tree match {
+ case TypeApply(asInstanceOf_? @ Select(base, _), _)
+ if asInstanceOf_?.symbol == defn.Any_asInstanceOf =>
+ Some(base)
+ case _ =>
+ Some(tree)
+ }
+ }
+
+ private object WrapArray {
+ lazy val isWrapArray: Set[Symbol] = {
+ val names = {
+ defn.ScalaValueClasses().map(sym => nme.wrapXArray(sym.name)) ++
+ Set(nme.wrapRefArray, nme.genericWrapArray)
+ }
+ names.map(defn.ScalaPredefModule.requiredMethod(_)).toSet
+ }
+
+ def unapply(tree: Apply): Option[Tree] = tree match {
+ case Apply(wrapArray_?, List(wrapped)) if isWrapArray(wrapArray_?.symbol) =>
+ Some(wrapped)
+ case _ =>
+ None
+ }
+ }
+
+ /** Gen JS code for loading a module.
+ *
* Can be given either the module symbol, or its module class symbol.
*/
private def genLoadModule(sym0: Symbol)(implicit pos: Position): js.Tree = {
@@ -1157,17 +2340,41 @@ class JSCodeGen()(implicit ctx: Context) {
if (sym1 == defn.StringModule) jsdefn.RuntimeStringModule.moduleClass
else sym1
- //val isGlobalScope = sym.tpe.typeSymbol isSubClass JSGlobalScopeClass
+ if (isJSType(sym)) {
+ if (isScalaJSDefinedJSClass(sym))
+ js.LoadJSModule(jstpe.ClassType(encodeClassFullName(sym)))
+ else if (sym.derivesFrom(jsdefn.JSGlobalScopeClass))
+ genLoadJSGlobal()
+ else
+ genLoadNativeJSModule(sym)
+ } else {
+ js.LoadModule(jstpe.ClassType(encodeClassFullName(sym)))
+ }
+ }
+
+ /** Gen JS code representing the constructor of a JS class. */
+ private def genLoadJSConstructor(sym: Symbol)(
+ implicit pos: Position): js.Tree = {
+ assert(!isStaticModule(sym) && !sym.is(Trait),
+ s"genPrimitiveJSClass called with non-class $sym")
+ js.LoadJSConstructor(jstpe.ClassType(encodeClassFullName(sym)))
+ }
- /*if (isGlobalScope) {
- genLoadGlobal()
- } else if (isJSNativeClass(sym)) {
- genPrimitiveJSModule(sym)
- } else {*/
- val cls = jstpe.ClassType(encodeClassFullName(sym))
- if (isRawJSType(sym)) js.LoadJSModule(cls)
- else js.LoadModule(cls)
- //}
+ /** Gen JS code representing a native JS module. */
+ private def genLoadNativeJSModule(sym: Symbol)(
+ implicit pos: Position): js.Tree = {
+ require(sym.is(ModuleClass),
+ s"genLoadNativeJSModule called with non-module $sym")
+ fullJSNameOf(sym).split('.').foldLeft(genLoadJSGlobal()) { (memo, chunk) =>
+ js.JSBracketSelect(memo, js.StringLiteral(chunk))
+ }
+ }
+
+ /** Gen JS code to load the JavaScript global scope. */
+ private def genLoadJSGlobal()(implicit pos: Position): js.Tree = {
+ js.JSBracketSelect(
+ js.JSBracketSelect(js.JSLinkingInfo(), js.StringLiteral("envInfo")),
+ js.StringLiteral("global"))
}
/** Generate a Class[_] value (e.g. coming from classOf[T]) */
@@ -1177,4 +2384,11 @@ class JSCodeGen()(implicit ctx: Context) {
private def isStaticModule(sym: Symbol): Boolean =
sym.is(Module) && sym.isStatic
+ private def isPrimitiveValueType(tpe: Type): Boolean = {
+ tpe.widenDealias match {
+ case JavaArrayType(_) => false
+ case t => t.typeSymbol.asClass.isPrimitiveValueClass
+ }
+ }
+
}
diff --git a/src/dotty/tools/backend/sjs/JSDefinitions.scala b/src/dotty/tools/backend/sjs/JSDefinitions.scala
index 0f4415b31..bd0b74031 100644
--- a/src/dotty/tools/backend/sjs/JSDefinitions.scala
+++ b/src/dotty/tools/backend/sjs/JSDefinitions.scala
@@ -5,6 +5,7 @@ import dotty.tools.dotc.core._
import Types._
import Contexts._
import Symbols._
+import Names._
import StdNames._
import Decorators._
@@ -172,4 +173,27 @@ final class JSDefinitions()(implicit ctx: Context) {
lazy val BoxesRunTime_unboxToCharR = defn.BoxesRunTimeModule.requiredMethodRef("unboxToChar")
def BoxesRunTime_unboxToChar(implicit ctx: Context): Symbol = BoxesRunTime_unboxToCharR.symbol
+ /** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
+ private def scalajsClassName(cls: Symbol)(implicit ctx: Context): TypeName =
+ if (cls.isClass && cls.owner == ScalaJSJSPackageClass) cls.asClass.name
+ else EmptyTypeName
+
+ /** Is the given `cls` a class of the form `scala.scalajs.js.prefixN` where
+ * `N` is a number.
+ *
+ * This is similar to `isVarArityClass` in `Definitions.scala`.
+ */
+ private def isScalaJSVarArityClass(cls: Symbol, prefix: Name): Boolean = {
+ val name = scalajsClassName(cls)
+ name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
+ }
+
+ def isJSFunctionClass(cls: Symbol): Boolean =
+ isScalaJSVarArityClass(cls, nme.Function)
+
+ private val ThisFunctionName = termName("ThisFunction")
+
+ def isJSThisFunctionClass(cls: Symbol): Boolean =
+ isScalaJSVarArityClass(cls, ThisFunctionName)
+
}
diff --git a/src/dotty/tools/backend/sjs/JSEncoding.scala b/src/dotty/tools/backend/sjs/JSEncoding.scala
index b35be3264..e8ea3258b 100644
--- a/src/dotty/tools/backend/sjs/JSEncoding.scala
+++ b/src/dotty/tools/backend/sjs/JSEncoding.scala
@@ -19,6 +19,7 @@ import ir.{Trees => js, Types => jstpe}
import ScopedVar.withScopedVars
import JSDefinitions._
+import JSInterop._
/** Encoding of symbol names for JavaScript
*
@@ -139,17 +140,32 @@ object JSEncoding {
* java.lang.String, which is the `this` parameter.
*/
def encodeRTStringMethodSym(sym: Symbol)(
- implicit ctx: Context, pos: ir.Position): (Symbol, js.Ident) = {
- require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym)
+ implicit ctx: Context, pos: ir.Position): js.Ident = {
require(sym.owner == defn.StringClass)
require(!sym.isClassConstructor && !sym.is(Flags.Private))
val (encodedName, paramsString) =
encodeMethodNameInternal(sym, inRTClass = true)
- val methodIdent = js.Ident(encodedName + paramsString,
+ js.Ident(encodedName + paramsString,
Some(sym.unexpandedName.decoded + paramsString))
+ }
+
+ /** Encodes a constructor symbol of java.lang.String for use in RuntimeString.
+ *
+ * - The name is rerouted to `newString`
+ * - The result type is set to `java.lang.String`
+ */
+ def encodeRTStringCtorSym(sym: Symbol)(
+ implicit ctx: Context, pos: ir.Position): js.Ident = {
+ require(sym.owner == defn.StringClass)
+ require(sym.isClassConstructor && !sym.is(Flags.Private))
- (jsdefn.RuntimeStringModuleClass, methodIdent)
+ val paramTypeNames = sym.info.firstParamTypes.map(internalName(_))
+ val paramAndResultTypeNames = paramTypeNames :+ ir.Definitions.StringClass
+ val paramsString = makeParamsString(paramAndResultTypeNames)
+
+ js.Ident("newString" + paramsString,
+ Some(sym.unexpandedName.decoded + paramsString))
}
private def encodeMethodNameInternal(sym: Symbol,
@@ -197,7 +213,7 @@ object JSEncoding {
def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = {
if (sym == defn.ObjectClass) jstpe.AnyType
- else if (isRawJSType(sym)) jstpe.AnyType
+ else if (isJSType(sym)) jstpe.AnyType
else {
assert(sym != defn.ArrayClass,
"encodeClassType() cannot be called with ArrayClass")
@@ -210,20 +226,17 @@ object JSEncoding {
js.Ident(encodeClassFullName(sym), Some(sym.fullName.toString))
}
- def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String =
- ir.Definitions.encodeClassName(sym.fullName.toString)
+ def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String = {
+ if (sym == defn.NothingClass) ir.Definitions.RuntimeNothingClass
+ else if (sym == defn.NullClass) ir.Definitions.RuntimeNullClass
+ else ir.Definitions.encodeClassName(sym.fullName.toString)
+ }
private def encodeMemberNameInternal(sym: Symbol)(
implicit ctx: Context): String = {
- sym.name.toString.replace("_", "$und")
+ sym.name.toString.replace("_", "$und").replace("~", "$tilde")
}
- def isRawJSType(sym: Symbol)(implicit ctx: Context): Boolean =
- sym.hasAnnotation(jsdefn.RawJSTypeAnnot)
-
- def isScalaJSDefinedJSClass(sym: Symbol)(implicit ctx: Context): Boolean =
- isRawJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot)
-
def toIRType(tp: Type)(implicit ctx: Context): jstpe.Type = {
val refType = toReferenceTypeInternal(tp)
refType._1 match {
@@ -243,7 +256,7 @@ object JSEncoding {
else
jstpe.IntType
} else {
- if (sym == defn.ObjectClass || isRawJSType(sym))
+ if (sym == defn.ObjectClass || isJSType(sym))
jstpe.AnyType
else if (sym == defn.NothingClass)
jstpe.NothingType
diff --git a/src/dotty/tools/backend/sjs/JSInterop.scala b/src/dotty/tools/backend/sjs/JSInterop.scala
new file mode 100644
index 000000000..6d66c3206
--- /dev/null
+++ b/src/dotty/tools/backend/sjs/JSInterop.scala
@@ -0,0 +1,110 @@
+package dotty.tools.backend.sjs
+
+import dotty.tools.dotc.core._
+import Contexts._
+import Flags._
+import Symbols._
+import NameOps._
+import StdNames._
+
+import JSDefinitions._
+
+/** Management of the interoperability with JavaScript. */
+object JSInterop {
+
+ /** Is this symbol a JavaScript type? */
+ def isJSType(sym: Symbol)(implicit ctx: Context): Boolean = {
+ //sym.hasAnnotation(jsdefn.RawJSTypeAnnot)
+ ctx.atPhase(ctx.erasurePhase) { implicit ctx =>
+ sym.derivesFrom(jsdefn.JSAnyClass)
+ }
+ }
+
+ /** Is this symbol a Scala.js-defined JS class, i.e., a non-native JS class? */
+ def isScalaJSDefinedJSClass(sym: Symbol)(implicit ctx: Context): Boolean =
+ isJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot)
+
+ /** Should this symbol be translated into a JS getter?
+ *
+ * This is true for any parameterless method, i.e., defined without `()`.
+ * Unlike `SymDenotations.isGetter`, it applies to user-defined methods as
+ * much as *accessor* methods created for `val`s and `var`s.
+ */
+ def isJSGetter(sym: Symbol)(implicit ctx: Context): Boolean = {
+ sym.info.firstParamTypes.isEmpty && ctx.atPhase(ctx.erasurePhase) { implicit ctx =>
+ sym.info.isParameterless
+ }
+ }
+
+ /** Should this symbol be translated into a JS setter?
+ *
+ * This is true for any method whose name ends in `_=`.
+ * Unlike `SymDenotations.isGetter`, it applies to user-defined methods as
+ * much as *accessor* methods created for `var`s.
+ */
+ def isJSSetter(sym: Symbol)(implicit ctx: Context): Boolean =
+ sym.name.isSetterName && sym.is(Method)
+
+ /** Should this symbol be translated into a JS bracket access?
+ *
+ * This is true for methods annotated with `@JSBracketAccess`.
+ */
+ def isJSBracketAccess(sym: Symbol)(implicit ctx: Context): Boolean =
+ sym.hasAnnotation(jsdefn.JSBracketAccessAnnot)
+
+ /** Should this symbol be translated into a JS bracket call?
+ *
+ * This is true for methods annotated with `@JSBracketCall`.
+ */
+ def isJSBracketCall(sym: Symbol)(implicit ctx: Context): Boolean =
+ sym.hasAnnotation(jsdefn.JSBracketCallAnnot)
+
+ /** Is this symbol a default param accessor for a JS method?
+ *
+ * For default param accessors of *constructors*, we need to test whether
+ * the companion *class* of the owner is a JS type; not whether the owner
+ * is a JS type.
+ */
+ def isJSDefaultParam(sym: Symbol)(implicit ctx: Context): Boolean = {
+ sym.name.isDefaultGetterName && {
+ val owner = sym.owner
+ if (owner.is(ModuleClass) &&
+ sym.name.asTermName.defaultGetterToMethod == nme.CONSTRUCTOR) {
+ isJSType(owner.linkedClass)
+ } else {
+ isJSType(owner)
+ }
+ }
+ }
+
+ /** Gets the unqualified JS name of a symbol.
+ *
+ * If it is not explicitly specified with an `@JSName` annotation, the
+ * JS name is inferred from the Scala name.
+ */
+ def jsNameOf(sym: Symbol)(implicit ctx: Context): String = {
+ sym.getAnnotation(jsdefn.JSNameAnnot).flatMap(_.argumentConstant(0)).fold {
+ val base = sym.name.unexpandedName.decode.toString.stripSuffix("_=")
+ if (sym.is(ModuleClass)) base.stripSuffix("$")
+ else if (!sym.is(Method)) base.stripSuffix(" ")
+ else base
+ } { constant =>
+ constant.stringValue
+ }
+ }
+
+ /** Gets the fully qualified JS name of a static class of module Symbol.
+ *
+ * This is the JS name of the symbol qualified by the fully qualified JS
+ * name of its original owner if the latter is a native JS object.
+ */
+ def fullJSNameOf(sym: Symbol)(implicit ctx: Context): String = {
+ assert(sym.isClass, s"fullJSNameOf called for non-class symbol $sym")
+ sym.getAnnotation(jsdefn.JSFullNameAnnot).flatMap(_.argumentConstant(0)).fold {
+ jsNameOf(sym)
+ } { constant =>
+ constant.stringValue
+ }
+ }
+
+}
diff --git a/src/dotty/tools/backend/sjs/JSPrimitives.scala b/src/dotty/tools/backend/sjs/JSPrimitives.scala
index 47d705fe8..52b5dc4b9 100644
--- a/src/dotty/tools/backend/sjs/JSPrimitives.scala
+++ b/src/dotty/tools/backend/sjs/JSPrimitives.scala
@@ -37,10 +37,13 @@ object JSPrimitives {
final val ENV_INFO = 316 // runtime.environmentInfo
final val LINKING_INFO = 317 // runtime.linkingInfo
+ final val THROW = 318 // <special-ops>.throw
+
}
class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
import JSPrimitives._
+ import scala.tools.nsc.backend.ScalaPrimitives._
private lazy val jsPrimitives: Map[Symbol, Int] = initJSPrimitives(ctx)
@@ -77,6 +80,18 @@ class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
val jsdefn = JSDefinitions.jsdefn
+ // For some reason, the JVM primitive set does not register those
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newBooleanArray")), NEW_ZARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newByteArray")), NEW_BARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newShortArray")), NEW_SARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newCharArray")), NEW_CARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newIntArray")), NEW_IARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newLongArray")), NEW_LARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newFloatArray")), NEW_FARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newDoubleArray")), NEW_DARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newRefArray")), NEW_OARRAY)
+ addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newUnitArray")), NEW_OARRAY)
+
addPrimitive(defn.Any_getClass, GETCLASS)
for (i <- 0 to 22)
@@ -107,6 +122,8 @@ class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
//addPrimitive(jsdefn.Runtime_environmentInfo, ENV_INFO)
//addPrimitive(jsdefn.Runtime_linkingInfo, LINKING_INFO)
+ addPrimitive(defn.throwMethod, THROW)
+
primitives.toMap
}
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index d9f1a3dca..fe16243bb 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -104,7 +104,11 @@ class Compiler {
def rootContext(implicit ctx: Context): Context = {
ctx.initialize()(ctx)
val actualPhases = if (ctx.settings.scalajs.value) {
- phases
+ // Remove phases that Scala.js does not want
+ phases.mapConserve(_.filter {
+ case _: FunctionalInterfaces => false
+ case _ => true
+ }).filter(_.nonEmpty)
} else {
// Remove Scala.js-related phases
phases.mapConserve(_.filter {
diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala
index 9972e3e64..ee808323a 100644
--- a/src/dotty/tools/dotc/Run.scala
+++ b/src/dotty/tools/dotc/Run.scala
@@ -8,6 +8,7 @@ import io.PlainFile
import util.{SourceFile, NoSource, Stats, SimpleMap}
import reporting.Reporter
import transform.TreeChecker
+import rewrite.Rewrites
import java.io.{BufferedWriter, OutputStreamWriter}
import scala.reflect.io.VirtualFile
import scala.util.control.NonFatal
@@ -64,6 +65,7 @@ class Run(comp: Compiler)(implicit ctx: Context) {
foreachUnit(printTree)
ctx.informTime(s"$phase ", start)
}
+ if (!ctx.reporter.hasErrors) Rewrites.writeBack()
}
private def printTree(ctx: Context) = {
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 8ba155097..2ab33a120 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -228,7 +228,7 @@ object desugar {
val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner))
.withMods(tdef.mods &~ PrivateLocal | ExpandedName)
val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam), tparams = Nil)
- .withFlags(PrivateLocalParamAccessor | Synthetic | tdef.mods.flags & VarianceFlags)
+ .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic)
Thicket(tparam, alias)
}
else tdef
@@ -237,15 +237,15 @@ object desugar {
@sharable private val synthetic = Modifiers(Synthetic)
private def toDefParam(tparam: TypeDef): TypeDef =
- tparam.withFlags(Param)
+ tparam.withMods(tparam.rawMods & EmptyFlags | Param)
private def toDefParam(vparam: ValDef): ValDef =
- vparam.withFlags(Param | vparam.rawMods.flags & Implicit)
+ vparam.withMods(vparam.rawMods & Implicit | Param)
/** The expansion of a class definition. See inline comments for what is involved */
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
val mods = cdef.mods
- val accessFlags = (mods.flags & AccessFlags).toCommonFlags
+ val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags)
val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match {
case meth: DefDef => (meth, Nil)
@@ -364,7 +364,7 @@ object desugar {
moduleDef(
ModuleDef(
name.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
- .withFlags(Synthetic | accessFlags))
+ .withMods(companionMods | Synthetic))
.withPos(cdef.pos).toList
// The companion object definitions, if a companion is needed, Nil otherwise.
@@ -421,10 +421,9 @@ object desugar {
// implicit wrapper is typechecked in same scope as constructor, so
// we can reuse the constructor parameters; no derived params are needed.
DefDef(name.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
- .withFlags(Synthetic | Implicit | accessFlags)
+ .withMods(companionMods | Synthetic | Implicit)
.withPos(cdef.pos) :: Nil
-
val self1 = {
val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
if (self.isEmpty) self
@@ -498,18 +497,18 @@ object desugar {
/** If `pat` is a variable pattern,
*
- * val/var p = e
+ * val/var/lazy val p = e
*
* Otherwise, in case there is exactly one variable x_1 in pattern
- * val/var p = e ==> val/var x_1 = (e: @unchecked) match (case p => (x_1))
+ * val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1))
*
* in case there are zero or more than one variables in pattern
- * val/var p = e ==> private synthetic val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
- * val/var x_1 = t$._1
+ * val/var/lazy p = e ==> private synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
+ * val/var/def x_1 = t$._1
* ...
- * val/var x_N = t$._N
+ * val/var/def x_N = t$._N
* If the original pattern variable carries a type annotation, so does the corresponding
- * ValDef.
+ * ValDef or DefDef.
*/
def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit ctx: Context): Tree = pat match {
case VarPattern(named, tpt) =>
@@ -533,12 +532,16 @@ object desugar {
derivedValDef(named, tpt, matchExpr, mods)
case _ =>
val tmpName = ctx.freshName().toTermName
- val patFlags = mods.flags & AccessFlags | Synthetic | (mods.flags & Lazy)
- val firstDef = ValDef(tmpName, TypeTree(), matchExpr).withFlags(patFlags)
+ val patMods = mods & (AccessFlags | Lazy) | Synthetic
+ val firstDef =
+ ValDef(tmpName, TypeTree(), matchExpr)
+ .withPos(pat.pos.union(rhs.pos)).withMods(patMods)
def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
val restDefs =
for (((named, tpt), n) <- vars.zipWithIndex)
- yield derivedValDef(named, tpt, selector(n), mods)
+ yield
+ if (mods is Lazy) derivedDefDef(named, tpt, selector(n), mods &~ Lazy)
+ else derivedValDef(named, tpt, selector(n), mods)
flatTree(firstDef :: restDefs)
}
}
@@ -635,6 +638,9 @@ object desugar {
private def derivedValDef(named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) =
ValDef(named.name.asTermName, tpt, rhs).withMods(mods).withPos(named.pos)
+ private def derivedDefDef(named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) =
+ DefDef(named.name.asTermName, Nil, Nil, tpt, rhs).withMods(mods).withPos(named.pos)
+
/** Main desugaring method */
def apply(tree: Tree)(implicit ctx: Context): Tree = {
@@ -1000,7 +1006,7 @@ object desugar {
collect(expr)
case NamedArg(_, arg) =>
collect(arg)
- case SeqLiteral(elems) =>
+ case SeqLiteral(elems, _) =>
elems foreach collect
case Alternative(trees) =>
for (tree <- trees; (vble, _) <- getVariables(tree))
diff --git a/src/dotty/tools/dotc/ast/NavigateAST.scala b/src/dotty/tools/dotc/ast/NavigateAST.scala
new file mode 100644
index 000000000..782866bad
--- /dev/null
+++ b/src/dotty/tools/dotc/ast/NavigateAST.scala
@@ -0,0 +1,83 @@
+package dotty.tools.dotc
+package ast
+
+import core.Contexts.Context
+import core.Decorators._
+import util.Positions._
+import Trees.{MemberDef, DefTree}
+
+/** Utility functions to go from typed to untyped ASTs */
+object NavigateAST {
+
+ /** The untyped tree corresponding to typed tree `tree` in the compilation
+ * unit specified by `ctx`
+ */
+ def toUntyped(tree: tpd.Tree)(implicit ctx: Context): untpd.Tree =
+ untypedPath(tree, exactMatch = true) match {
+ case (utree: untpd.Tree) :: _ =>
+ utree
+ case _ =>
+ val loosePath = untypedPath(tree, exactMatch = false)
+ throw new
+ Error(i"""no untyped tree for $tree, pos = ${tree.pos}, envelope = ${tree.envelope}
+ |best matching path =\n$loosePath%\n====\n%
+ |path positions = ${loosePath.map(_.pos)}
+ |path envelopes = ${loosePath.map(_.envelope)}""".stripMargin)
+ }
+
+ /** The reverse path of untyped trees starting with a tree that closest matches
+ * `tree` and ending in the untyped tree at the root of the compilation unit
+ * specified by `ctx`.
+ * @param exactMatch If `true`, the path must start with a node that exactly
+ * matches `tree`, or `Nil` is returned.
+ * If `false` the path might start with a node enclosing
+ * the logical position of `tree`.
+ * Note: A complication concerns member definitions. ValDefs and DefDefs
+ * have after desugaring a position that spans just the name of the symbol being
+ * defined and nothing else. So we look instead for an untyped tree approximating the
+ * envelope of the definition, and declare success if we find another DefTree.
+ */
+ def untypedPath(tree: tpd.Tree, exactMatch: Boolean = false)(implicit ctx: Context): List[Positioned] =
+ tree match {
+ case tree: MemberDef[_] =>
+ untypedPath(tree.envelope) match {
+ case path @ (last: DefTree[_]) :: _ => path
+ case path if !exactMatch => path
+ case _ => Nil
+ }
+ case _ =>
+ untypedPath(tree.pos) match {
+ case (path @ last :: _) if last.pos == tree.pos || !exactMatch => path
+ case _ => Nil
+ }
+ }
+
+ /** The reverse part of the untyped root of the compilation unit of `ctx` to
+ * position `pos`.
+ */
+ def untypedPath(pos: Position)(implicit ctx: Context): List[Positioned] =
+ pathTo(pos, ctx.compilationUnit.untpdTree)
+
+
+ /** The reverse path from node `from` to the node that closest encloses position `pos`,
+ * or `Nil` if no such path exists. If a non-empty path is returned it starts with
+ * the node closest enclosing `pos` and ends with `from`.
+ */
+ def pathTo(pos: Position, from: Positioned)(implicit ctx: Context): List[Positioned] = {
+ def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
+ while (it.hasNext) {
+ val path1 = it.next match {
+ case p: Positioned => singlePath(p, path)
+ case xs: List[_] => childPath(xs.iterator, path)
+ case _ => path
+ }
+ if (path1 ne path) return path1
+ }
+ path
+ }
+ def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] =
+ if (p.envelope contains pos) childPath(p.productIterator, p :: path)
+ else path
+ singlePath(from, Nil)
+ }
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/ast/Positioned.scala b/src/dotty/tools/dotc/ast/Positioned.scala
index e0bd6c75a..e7f5de591 100644
--- a/src/dotty/tools/dotc/ast/Positioned.scala
+++ b/src/dotty/tools/dotc/ast/Positioned.scala
@@ -54,21 +54,55 @@ abstract class Positioned extends DotClass with Product {
*/
private[dotc] def setPosUnchecked(pos: Position) = curPos = pos
- /** If any children of this node do not have positions, set them to the given position,
+ /** If any children of this node do not have positions,
+ * fit their positions between the positions of the known subtrees
* and transitively visit their children.
+ * The method is likely time-critical because it is invoked on any node
+ * we create, so we want to avoid object allocations in the common case.
+ * The method is naturally expressed as two mutually (tail-)recursive
+ * functions, one which computes the next element to consider or terminates if there
+ * is none and the other which propagates the position information to that element.
+ * But since mutual tail recursion is not supported in Scala, we express it instead
+ * as a while loop with a termination by return in the middle.
*/
private def setChildPositions(pos: Position): Unit = {
- def deepSetPos(x: Any): Unit = x match {
- case p: Positioned =>
- if (!p.pos.exists) p.setPos(pos)
- case xs: List[_] =>
- xs foreach deepSetPos
- case _ =>
+ var n = productArity // subnodes are analyzed right to left
+ var elems: List[Any] = Nil // children in lists still to be considered, from right to left
+ var end = pos.end // the last defined offset, fill in positions up to this offset
+ var outstanding: List[Positioned] = Nil // nodes that need their positions filled once a start position
+ // is known, from left to right.
+ def fillIn(ps: List[Positioned], start: Int, end: Int): Unit = ps match {
+ case p :: ps1 =>
+ p.setPos(Position(start, end))
+ fillIn(ps1, end, end)
+ case nil =>
}
- var n = productArity
- while (n > 0) {
- n -= 1
- deepSetPos(productElement(n))
+ while (true) {
+ var nextChild: Any = null // the next child to be considered
+ if (elems.nonEmpty) {
+ nextChild = elems.head
+ elems = elems.tail
+ }
+ else if (n > 0) {
+ n = n - 1
+ nextChild = productElement(n)
+ }
+ else {
+ fillIn(outstanding, pos.start, end)
+ return
+ }
+ nextChild match {
+ case p: Positioned =>
+ if (p.pos.exists) {
+ fillIn(outstanding, p.pos.end, end)
+ outstanding = Nil
+ end = p.pos.start
+ }
+ else outstanding = p :: outstanding
+ case xs: List[_] =>
+ elems = elems ::: xs.reverse
+ case _ =>
+ }
}
}
@@ -114,26 +148,4 @@ abstract class Positioned extends DotClass with Product {
found
}
}
-
- /** The path from this node to `that` node, represented
- * as a list starting with `this`, ending with`that` where
- * every node is a child of its predecessor.
- * Nil if no such path exists.
- */
- def pathTo(that: Positioned): List[Positioned] = {
- def childPath(it: Iterator[Any]): List[Positioned] =
- if (it.hasNext) {
- val cpath = it.next match {
- case x: Positioned => x.pathTo(that)
- case xs: List[_] => childPath(xs.iterator)
- case _ => Nil
- }
- if (cpath.nonEmpty) cpath else childPath(it)
- } else Nil
- if (this eq that) this :: Nil
- else if (this.envelope contains that.pos) {
- val cpath = childPath(productIterator)
- if (cpath.nonEmpty) this :: cpath else Nil
- } else Nil
- }
}
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index 54ace3be4..d0197b443 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -50,13 +50,17 @@ object Trees {
def toTypeFlags: Modifiers[T] = withFlags(flags.toTypeFlags)
def toTermFlags: Modifiers[T] = withFlags(flags.toTermFlags)
- private def withFlags(flags: FlagSet) =
+ def withFlags(flags: FlagSet) =
if (this.flags == flags) this
else copy(flags = flags)
+ def withAddedAnnotation[U >: Untyped <: T](annot: Tree[U]): Modifiers[U] =
+ if (annotations.exists(_ eq annot)) this
+ else withAnnotations(annotations :+ annot)
+
def withAnnotations[U >: Untyped <: T](annots: List[Tree[U]]): Modifiers[U] =
- if (annots.isEmpty) this
- else copy(annotations = annotations ++ annots)
+ if (annots eq annotations) this
+ else copy(annotations = annots)
def withPrivateWithin(pw: TypeName) =
if (pw.isEmpty) this
@@ -515,16 +519,18 @@ object Trees {
type ThisTree[-T >: Untyped] = Try[T]
}
- /** Seq(elems) */
- case class SeqLiteral[-T >: Untyped] private[ast] (elems: List[Tree[T]])
+ /** Seq(elems)
+ * @param tpt The element type of the sequence.
+ */
+ case class SeqLiteral[-T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])
extends Tree[T] {
type ThisTree[-T >: Untyped] = SeqLiteral[T]
}
/** Array(elems) */
- class JavaSeqLiteral[T >: Untyped] private[ast] (elems: List[Tree[T]])
- extends SeqLiteral(elems) {
- override def toString = s"JavaSeqLiteral($elems)"
+ class JavaSeqLiteral[T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])
+ extends SeqLiteral(elems, elemtpt) {
+ override def toString = s"JavaSeqLiteral($elems, $elemtpt)"
}
/** A type tree that represents an existing or inferred type */
@@ -974,12 +980,12 @@ object Trees {
case tree: Try if (expr eq tree.expr) && (cases eq tree.cases) && (finalizer eq tree.finalizer) => tree
case _ => finalize(tree, untpd.Try(expr, cases, finalizer))
}
- def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = tree match {
+ def SeqLiteral(tree: Tree)(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = tree match {
case tree: JavaSeqLiteral =>
- if (elems eq tree.elems) tree
- else finalize(tree, new JavaSeqLiteral(elems))
- case tree: SeqLiteral if elems eq tree.elems => tree
- case _ => finalize(tree, untpd.SeqLiteral(elems))
+ if ((elems eq tree.elems) && (elemtpt eq tree.elemtpt)) tree
+ else finalize(tree, new JavaSeqLiteral(elems, elemtpt))
+ case tree: SeqLiteral if (elems eq tree.elems) && (elemtpt eq tree.elemtpt) => tree
+ case _ => finalize(tree, untpd.SeqLiteral(elems, elemtpt))
}
def TypeTree(tree: Tree)(original: Tree): TypeTree = tree match {
case tree: TypeTree if original eq tree.original => tree
@@ -1125,8 +1131,8 @@ object Trees {
cpy.Return(tree)(transform(expr), transformSub(from))
case Try(block, cases, finalizer) =>
cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer))
- case SeqLiteral(elems) =>
- cpy.SeqLiteral(tree)(transform(elems))
+ case SeqLiteral(elems, elemtpt) =>
+ cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt))
case TypeTree(original) =>
tree
case SingletonTypeTree(ref) =>
@@ -1229,8 +1235,8 @@ object Trees {
this(this(x, expr), from)
case Try(block, handler, finalizer) =>
this(this(this(x, block), handler), finalizer)
- case SeqLiteral(elems) =>
- this(x, elems)
+ case SeqLiteral(elems, elemtpt) =>
+ this(this(x, elems), elemtpt)
case TypeTree(original) =>
x
case SingletonTypeTree(ref) =>
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index bf6084d7a..a6d97478b 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -123,14 +123,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Try(block: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try =
ta.assignType(untpd.Try(block, cases, finalizer), block, cases)
- def SeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral =
- ta.assignType(untpd.SeqLiteral(elems), elems)
+ def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral =
+ ta.assignType(untpd.SeqLiteral(elems, elemtpt), elems, elemtpt)
- def SeqLiteral(tpe: Type, elems: List[Tree])(implicit ctx: Context): SeqLiteral =
- if (tpe derivesFrom defn.SeqClass) SeqLiteral(elems) else JavaSeqLiteral(elems)
-
- def JavaSeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral =
- ta.assignType(new untpd.JavaSeqLiteral(elems), elems)
+ def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral =
+ ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt)
def TypeTree(original: Tree)(implicit ctx: Context): TypeTree =
TypeTree(original.tpe, original)
@@ -564,11 +561,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
}
- override def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = {
- val tree1 = untpd.cpy.SeqLiteral(tree)(elems)
+ override def SeqLiteral(tree: Tree)(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = {
+ val tree1 = untpd.cpy.SeqLiteral(tree)(elems, elemtpt)
tree match {
- case tree: SeqLiteral if sameTypes(elems, tree.elems) => tree1.withTypeUnchecked(tree.tpe)
- case _ => ta.assignType(tree1, elems)
+ case tree: SeqLiteral
+ if sameTypes(elems, tree.elems) && (elemtpt.tpe eq tree.elemtpt.tpe) =>
+ tree1.withTypeUnchecked(tree.tpe)
+ case _ =>
+ ta.assignType(tree1, elems, elemtpt)
}
}
@@ -852,7 +852,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
ctx.typer.resolveOverloaded(allAlts, proto, Nil)
assert(alternatives.size == 1,
i"${if (alternatives.isEmpty) "no" else "multiple"} overloads available for " +
- i"$method on ${receiver.tpe.widenDealias} with targs: $targs, args: $args and expectedType: $expectedType." +
+ i"$method on ${receiver.tpe.widenDealias} with targs: $targs%, %; args: $args%, % of types ${args.tpes}%, %; expectedType: $expectedType." +
i" isAnnotConstructor = $isAnnotConstructor.\n" +
i"all alternatives: ${allAlts.map(_.symbol.showDcl).mkString(", ")}\n" +
i"matching alternatives: ${alternatives.map(_.symbol.showDcl).mkString(", ")}.") // this is parsed from bytecode tree. there's nothing user can do about it
diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala
index 85052a4da..c7a7036c3 100644
--- a/src/dotty/tools/dotc/ast/untpd.scala
+++ b/src/dotty/tools/dotc/ast/untpd.scala
@@ -127,8 +127,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
def Return(expr: Tree, from: Tree): Return = new Return(expr, from)
def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree): Try = new Try(expr, cases, finalizer)
- def SeqLiteral(elems: List[Tree]): SeqLiteral = new SeqLiteral(elems)
- def JavaSeqLiteral(elems: List[Tree]): JavaSeqLiteral = new JavaSeqLiteral(elems)
+ def SeqLiteral(elems: List[Tree], elemtpt: Tree): SeqLiteral = new SeqLiteral(elems, elemtpt)
+ def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt)
def TypeTree(original: Tree): TypeTree = new TypeTree(original)
def TypeTree() = new TypeTree(EmptyTree)
def SingletonTypeTree(ref: Tree): SingletonTypeTree = new SingletonTypeTree(ref)
diff --git a/src/dotty/tools/dotc/config/JavaPlatform.scala b/src/dotty/tools/dotc/config/JavaPlatform.scala
index 432a9a0b7..433b5b3f0 100644
--- a/src/dotty/tools/dotc/config/JavaPlatform.scala
+++ b/src/dotty/tools/dotc/config/JavaPlatform.scala
@@ -7,6 +7,7 @@ import ClassPath.{ JavaContext, DefaultJavaContext }
import core._
import Symbols._, Types._, Contexts._, Denotations._, SymDenotations._, StdNames._, Names._
import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._
+import transform.ExplicitOuter, transform.SymUtils._
class JavaPlatform extends Platform {
@@ -38,6 +39,14 @@ class JavaPlatform extends Platform {
def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = new ctx.base.loaders.PackageLoader(root, classPath)
+ /** Is the SAMType `cls` also a SAM under the rules of the JVM? */
+ def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ cls.is(NoInitsTrait) &&
+ cls.superClass == defn.ObjectClass &&
+ cls.directlyInheritedTraits.forall(_.is(NoInits)) &&
+ !ExplicitOuter.needsOuterIfReferenced(cls) &&
+ cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary
+
/** We could get away with excluding BoxedBooleanClass for the
* purpose of equality testing since it need not compare equal
* to anything but other booleans, but it should be present in
diff --git a/src/dotty/tools/dotc/config/Platform.scala b/src/dotty/tools/dotc/config/Platform.scala
index 972892d12..062d9002d 100644
--- a/src/dotty/tools/dotc/config/Platform.scala
+++ b/src/dotty/tools/dotc/config/Platform.scala
@@ -27,6 +27,9 @@ abstract class Platform {
/** Any platform-specific phases. */
//def platformPhases: List[SubComponent]
+ /** Is the SAMType `cls` also a SAM under the rules of the platform? */
+ def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean
+
/** The various ways a boxed primitive might materialize at runtime. */
def isMaybeBoxed(sym: ClassSymbol)(implicit ctx: Context): Boolean
diff --git a/src/dotty/tools/dotc/config/SJSPlatform.scala b/src/dotty/tools/dotc/config/SJSPlatform.scala
index fec9c25a1..3ec8049ae 100644
--- a/src/dotty/tools/dotc/config/SJSPlatform.scala
+++ b/src/dotty/tools/dotc/config/SJSPlatform.scala
@@ -2,6 +2,7 @@ package dotty.tools.dotc.config
import dotty.tools.dotc.core._
import Contexts._
+import Symbols._
import dotty.tools.backend.sjs.JSDefinitions
@@ -10,4 +11,8 @@ class SJSPlatform()(implicit ctx: Context) extends JavaPlatform {
/** Scala.js-specific definitions. */
val jsDefinitions: JSDefinitions = new JSDefinitions()
+ /** Is the SAMType `cls` also a SAM under the rules of the Scala.js back-end? */
+ override def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ defn.isFunctionClass(cls) || jsDefinitions.isJSFunctionClass(cls)
+
}
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index b1d53c90d..07a23fdb6 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -1,6 +1,8 @@
-package dotty.tools.dotc.config
+package dotty.tools.dotc
+package config
import PathResolver.Defaults
+import rewrite.Rewrites
class ScalaSettings extends Settings.SettingGroup {
@@ -48,6 +50,7 @@ class ScalaSettings extends Settings.SettingGroup {
val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.")
val language = MultiStringSetting("-language", "feature", "Enable one or more language features.")
+ val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax")
/** -X "Advanced" settings
*/
diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala
index c45289c4c..f30cedaa0 100644
--- a/src/dotty/tools/dotc/config/Settings.scala
+++ b/src/dotty/tools/dotc/config/Settings.scala
@@ -19,6 +19,7 @@ object Settings {
val StringTag = ClassTag(classOf[String])
val ListTag = ClassTag(classOf[List[_]])
val VersionTag = ClassTag(classOf[ScalaVersion])
+ val OptionTag = ClassTag(classOf[Option[_]])
class SettingsState(initialValues: Seq[Any]) {
private var values = ArrayBuffer(initialValues: _*)
@@ -55,7 +56,8 @@ object Settings {
choices: Seq[T] = Nil,
prefix: String = "",
aliases: List[String] = Nil,
- depends: List[(Setting[_], Any)] = Nil)(private[Settings] val idx: Int) {
+ depends: List[(Setting[_], Any)] = Nil,
+ propertyClass: Option[Class[_]] = None)(private[Settings] val idx: Int) {
def withAbbreviation(abbrv: String): Setting[T] =
copy(aliases = aliases :+ abbrv)(idx)
@@ -112,6 +114,8 @@ object Settings {
def doSet(argRest: String) = ((implicitly[ClassTag[T]], args): @unchecked) match {
case (BooleanTag, _) =>
update(true, args)
+ case (OptionTag, _) =>
+ update(Some(propertyClass.get.newInstance), args)
case (ListTag, _) =>
if (argRest.isEmpty) missingArg
else update((argRest split ",").toList, args)
@@ -257,5 +261,8 @@ object Settings {
def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] =
publish(Setting(name, descr, default))
+
+ def OptionSetting[T: ClassTag](name: String, descr: String): Setting[Option[T]] =
+ publish(Setting(name, descr, None, propertyClass = Some(implicitly[ClassTag[T]].runtimeClass)))
}
}
diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala
index b60f437d5..ce87506ae 100644
--- a/src/dotty/tools/dotc/core/Phases.scala
+++ b/src/dotty/tools/dotc/core/Phases.scala
@@ -233,8 +233,10 @@ object Phases {
private val picklerCache = new PhaseCache(classOf[Pickler])
private val refChecksCache = new PhaseCache(classOf[RefChecks])
+ private val elimRepeatedCache = new PhaseCache(classOf[ElimRepeated])
private val extensionMethodsCache = new PhaseCache(classOf[ExtensionMethods])
private val erasureCache = new PhaseCache(classOf[Erasure])
+ private val elimErasedValueTypeCache = new PhaseCache(classOf[ElimErasedValueType])
private val patmatCache = new PhaseCache(classOf[PatternMatcher])
private val lambdaLiftCache = new PhaseCache(classOf[LambdaLift])
private val flattenCache = new PhaseCache(classOf[Flatten])
@@ -245,8 +247,10 @@ object Phases {
def typerPhase = typerCache.phase
def picklerPhase = picklerCache.phase
def refchecksPhase = refChecksCache.phase
+ def elimRepeatedPhase = elimRepeatedCache.phase
def extensionMethodsPhase = extensionMethodsCache.phase
def erasurePhase = erasureCache.phase
+ def elimErasedValueTypePhase = elimErasedValueTypeCache.phase
def patmatPhase = patmatCache.phase
def lambdaLiftPhase = lambdaLiftCache.phase
def flattenPhase = flattenCache.phase
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index bde8cc10a..a83e7726a 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -616,7 +616,7 @@ object SymDenotations {
/** Is this symbol a class references to which that are supertypes of null? */
final def isNullableClass(implicit ctx: Context): Boolean =
- isClass && !isValueClass && !(this is ModuleClass)
+ isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass
/** Is this definition accessible as a member of tree with type `pre`?
* @param pre The type of the tree from which the selection is made
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index 734d31858..371be1586 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -546,7 +546,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
hasImport(c)
}))
def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_")
- hasImport || hasOption
+ hasImport(ctx.withPhase(ctx.typerPhase)) || hasOption
}
/** Is auto-tupling enabled? */
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 17c2ec4ca..3801f1914 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -2424,8 +2424,6 @@ object Types {
x => paramBounds mapConserve (_.subst(this, x).bounds),
x => resType.subst(this, x))
- // need to override hashCode and equals to be object identity
- // because paramNames by itself is not discriminatory enough
override def equals(other: Any) = other match {
case other: PolyType =>
other.paramNames == this.paramNames && other.paramBounds == this.paramBounds && other.resType == this.resType
diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
index 9ea24324b..25558a79a 100644
--- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
+++ b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
@@ -438,7 +438,14 @@ class ClassfileParser(
case None => hasError = true
}
if (hasError) None
- else if (skip) None else Some(JavaSeqLiteral(arr.toList))
+ else if (skip) None
+ else {
+ val elems = arr.toList
+ val elemType =
+ if (elems.isEmpty) defn.ObjectType
+ else ctx.typeComparer.lub(elems.tpes).widen
+ Some(JavaSeqLiteral(elems, TypeTree(elemType)))
+ }
case ANNOTATION_TAG =>
parseAnnotation(index, skip) map (_.tree)
}
diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index 7f8e5cef3..f8f9c993f 100644
--- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -418,9 +418,9 @@ class TreePickler(pickler: TastyPickler) {
case Try(block, cases, finalizer) =>
writeByte(TRY)
withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeUnlessEmpty(finalizer) }
- case SeqLiteral(elems) =>
+ case SeqLiteral(elems, elemtpt) =>
writeByte(REPEATED)
- withLength { elems.foreach(pickleTree) }
+ withLength { pickleTree(elemtpt); elems.foreach(pickleTree) }
case TypeTree(original) =>
pickleTpt(tree)
case Bind(name, body) =>
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index 016aa41de..eb3369184 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -805,7 +805,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
val fn = readTerm()
val isJava = fn.tpe.isInstanceOf[JavaMethodType]
def readArg() = readTerm() match {
- case SeqLiteral(elems) if isJava => JavaSeqLiteral(elems)
+ case SeqLiteral(elems, elemtpt) if isJava => JavaSeqLiteral(elems, elemtpt)
case arg => arg
}
tpd.Apply(fn, until(end)(readArg()))
@@ -841,7 +841,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
case TRY =>
Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree))
case REPEATED =>
- SeqLiteral(until(end)(readTerm()))
+ val elemtpt = readTpt()
+ SeqLiteral(until(end)(readTerm()), elemtpt)
case BIND =>
val name = readName()
val info = readType()
diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
index 7a13388ae..2831de3e0 100644
--- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
+++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
@@ -850,8 +850,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
val end = readNat() + readIndex
// array elements are trees representing instances of scala.annotation.Annotation
SeqLiteral(
- defn.SeqType.appliedTo(defn.AnnotationType :: Nil),
- until(end, () => readClassfileAnnotArg(readNat())))
+ until(end, () => readClassfileAnnotArg(readNat())),
+ TypeTree(defn.AnnotationType))
}
private def readAnnotInfoArg()(implicit ctx: Context): Tree = {
@@ -1063,7 +1063,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
case ARRAYVALUEtree =>
val elemtpt = readTreeRef()
val trees = until(end, readTreeRef)
- SeqLiteral(defn.SeqType.appliedTo(elemtpt.tpe :: Nil), trees)
+ SeqLiteral(trees, elemtpt)
// note can't deal with trees passed to Java methods as arrays here
case FUNCTIONtree =>
diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala
index be7822cdc..fbb362354 100644
--- a/src/dotty/tools/dotc/parsing/JavaParsers.scala
+++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala
@@ -175,6 +175,8 @@ object JavaParsers {
/** Consume one token of the specified type, or
* signal an error if it is not there.
+ *
+ * @return The offset at the start of the token to accept
*/
def accept(token: Int): Int = {
val offset = in.offset
@@ -511,7 +513,7 @@ object JavaParsers {
atPos(offset) {
New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil)
}
- mods1 = mods1 withAnnotations annot :: Nil
+ mods1 = mods1 withAddedAnnotation annot
skipTo(SEMI)
accept(SEMI)
unimplementedExpr
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index bb8fbe08b..44a70886e 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -21,6 +21,7 @@ import Constants._
import ScriptParsers._
import annotation.switch
import util.DotClass
+import rewrite.Rewrites.patch
object Parsers {
@@ -226,6 +227,8 @@ object Parsers {
/** Consume one token of the specified type, or
* signal an error if it is not there.
+ *
+ * @return The offset at the start of the token to accept
*/
def accept(token: Int): Int = {
val offset = in.offset
@@ -233,7 +236,7 @@ object Parsers {
syntaxErrorOrIncomplete(expectedMsg(token))
}
if (in.token == token) in.nextToken()
- in.offset
+ offset
}
/** semi = nl {nl} | `;'
@@ -258,11 +261,19 @@ object Parsers {
} finally inFunReturnType = saved
}
+ private val isScala2Mode =
+ ctx.settings.language.value.contains(nme.Scala2.toString)
+
+ def migrationWarningOrError(msg: String, offset: Int = in.offset) =
+ if (isScala2Mode)
+ ctx.migrationWarning(msg, source atPos Position(offset))
+ else
+ syntaxError(msg, offset)
+
/** Cannot use ctx.featureEnabled because accessing the context would force too much */
private def testScala2Mode(msg: String, pos: Position = Position(in.offset)) = {
- val s2 = ctx.settings.language.value.contains(nme.Scala2.toString)
- if (s2) ctx.migrationWarning(msg, source atPos pos)
- s2
+ if (isScala2Mode) ctx.migrationWarning(msg, source atPos pos)
+ isScala2Mode
}
/* ---------- TREE CONSTRUCTION ------------------------------------------- */
@@ -1010,7 +1021,7 @@ object Parsers {
val uscoreStart = in.skipToken()
if (isIdent(nme.raw.STAR)) {
in.nextToken()
- if (in.token != RPAREN) syntaxError("`_*' can be used only for last argument")
+ if (in.token != RPAREN) syntaxError("`_*' can be used only for last argument", uscoreStart)
Typed(t, atPos(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
} else {
syntaxErrorOrIncomplete("`*' expected"); t
@@ -1308,7 +1319,20 @@ object Parsers {
*/
val pattern2 = () => infixPattern() match {
case p @ Ident(name) if isVarPattern(p) && in.token == AT =>
- atPos(p.pos.start, in.skipToken()) { Bind(name, infixPattern()) }
+ val pos = in.skipToken()
+
+ // compatibility for Scala2 `x @ _*` syntax
+ infixPattern() match {
+ case pt @ Ident(tpnme.WILDCARD_STAR) =>
+ migrationWarningOrError("The syntax `x @ _*' is no longer supported; use `x : _*' instead", p.pos.start)
+ atPos(p.pos.start, pos) { Typed(p, pt) }
+ case p =>
+ atPos(p.pos.start, pos) { Bind(name, p) }
+ }
+ case p @ Ident(tpnme.WILDCARD_STAR) =>
+ // compatibility for Scala2 `_*` syntax
+ migrationWarningOrError("The syntax `_*' is no longer supported; use `x : _*' instead", p.pos.start)
+ atPos(p.pos.start) { Typed(Ident(nme.WILDCARD), p) }
case p =>
p
}
@@ -1336,7 +1360,15 @@ object Parsers {
case t => simplePatternRest(t)
}
case USCORE =>
- wildcardIdent()
+ val wildIndent = wildcardIdent()
+
+ // compatibility for Scala2 `x @ _*` and `_*` syntax
+ // `x: _*' is parsed in `ascription'
+ if (isIdent(nme.raw.STAR)) {
+ in.nextToken()
+ if (in.token != RPAREN) syntaxError("`_*' can be used only for last argument", wildIndent.pos)
+ atPos(wildIndent.pos) { Ident(tpnme.WILDCARD_STAR) }
+ } else wildIndent
case LPAREN =>
atPos(in.offset) { makeTupleOrParens(inParens(patternsOpt())) }
case LBRACE =>
@@ -1761,13 +1793,20 @@ object Parsers {
* DefSig ::= id [DefTypeParamClause] ParamClauses
*/
def defDefOrDcl(mods: Modifiers): Tree = atPos(tokenRange) {
- def scala2ProcedureSyntax =
- testScala2Mode("Procedure syntax no longer supported; `: Unit =' should be inserted here")
+ def scala2ProcedureSyntax(resultTypeStr: String) = {
+ val toInsert =
+ if (in.token == LBRACE) s"$resultTypeStr ="
+ else ": Unit " // trailing space ensures that `def f()def g()` works.
+ testScala2Mode(s"Procedure syntax no longer supported; `$toInsert' should be inserted here") && {
+ patch(source, Position(in.lastOffset), toInsert)
+ true
+ }
+ }
if (in.token == THIS) {
in.nextToken()
val vparamss = paramClauses(nme.CONSTRUCTOR)
val rhs = {
- if (!(in.token == LBRACE && scala2ProcedureSyntax)) accept(EQUALS)
+ if (!(in.token == LBRACE && scala2ProcedureSyntax(""))) accept(EQUALS)
atPos(in.offset) { constrExpr() }
}
makeConstructor(Nil, vparamss, rhs).withMods(mods)
@@ -1784,7 +1823,7 @@ object Parsers {
}
else if (!tpt.isEmpty)
EmptyTree
- else if (scala2ProcedureSyntax) {
+ else if (scala2ProcedureSyntax(": Unit")) {
tpt = scalaUnit
if (in.token == LBRACE) expr()
else EmptyTree
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index a03a01e8d..e21f12410 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -380,8 +380,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
changePrec(GlobalPrec) {
"throw " ~ toText(expr)
}
- case SeqLiteral(elems) =>
- "[" ~ toTextGlobal(elems, ",") ~ "]"
+ case SeqLiteral(elems, elemtpt) =>
+ "[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
case tpt: untpd.DerivedTypeTree =>
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
case TypeTree(orig) =>
diff --git a/src/dotty/tools/dotc/rewrite/Rewrites.scala b/src/dotty/tools/dotc/rewrite/Rewrites.scala
new file mode 100644
index 000000000..7ab0e5d59
--- /dev/null
+++ b/src/dotty/tools/dotc/rewrite/Rewrites.scala
@@ -0,0 +1,92 @@
+package dotty.tools.dotc
+package rewrite
+
+import util.{SourceFile, Positions}
+import Positions.Position
+import core.Contexts.{Context, FreshContext}
+import collection.mutable
+
+/** Handles rewriting of Scala2 files to Dotty */
+object Rewrites {
+ private class PatchedFiles extends mutable.HashMap[SourceFile, Patches]
+
+ private case class Patch(pos: Position, replacement: String) {
+ def delta = replacement.length - (pos.end - pos.start)
+ }
+
+ private class Patches(source: SourceFile) {
+ private val pbuf = new mutable.ListBuffer[Patch]()
+
+ def addPatch(pos: Position, replacement: String): Unit =
+ pbuf += Patch(pos, replacement)
+
+ def apply(cs: Array[Char]): Array[Char] = {
+ val delta = pbuf.map(_.delta).sum
+ val patches = pbuf.toList.sortBy(_.pos.start)
+ if (patches.nonEmpty)
+ patches reduceLeft {(p1, p2) =>
+ assert(p1.pos.end <= p2.pos.start, s"overlapping patches: $p1 and $p2")
+ p2
+ }
+ val ds = new Array[Char](cs.length + delta)
+ def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
+ def copy(upTo: Int): Int = {
+ val untouched = upTo - inIdx
+ Array.copy(cs, inIdx, ds, outIdx, untouched)
+ outIdx + untouched
+ }
+ ps match {
+ case patch @ Patch(pos, replacement) :: ps1 =>
+ val outNew = copy(pos.start)
+ replacement.copyToArray(ds, outNew)
+ loop(ps1, pos.end, outNew + replacement.length)
+ case Nil =>
+ val outNew = copy(cs.length)
+ assert(outNew == ds.length, s"$outNew != ${ds.length}")
+ }
+ }
+ loop(patches, 0, 0)
+ ds
+ }
+
+ def writeBack(): Unit = {
+ val out = source.file.output
+ val chars = apply(source.underlying.content)
+ val bytes = new String(chars).getBytes
+ out.write(bytes)
+ out.close()
+ }
+ }
+
+ /** If -rewrite is set, record a patch that replaces the range
+ * given by `pos` in `source` by `replacement`
+ */
+ def patch(source: SourceFile, pos: Position, replacement: String)(implicit ctx: Context): Unit =
+ for (rewrites <- ctx.settings.rewrite.value)
+ rewrites.patched
+ .getOrElseUpdate(source, new Patches(source))
+ .addPatch(pos, replacement)
+
+ /** Patch position in `ctx.compilationUnit.source`. */
+ def patch(pos: Position, replacement: String)(implicit ctx: Context): Unit =
+ patch(ctx.compilationUnit.source, pos, replacement)
+
+ /** If -rewrite is set, apply all patches and overwrite patched source files.
+ */
+ def writeBack()(implicit ctx: Context) =
+ for (rewrites <- ctx.settings.rewrite.value; source <- rewrites.patched.keys) {
+ ctx.println(s"[patched file ${source.file.path}]")
+ rewrites.patched(source).writeBack()
+ }
+}
+
+/** A completely encapsulated class representing rewrite state, used
+ * as an optional setting.
+ */
+class Rewrites {
+ import Rewrites._
+ private val patched = new PatchedFiles
+}
+
+
+
diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala
index 98abbb26d..30778267d 100644
--- a/src/dotty/tools/dotc/transform/ElimRepeated.scala
+++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala
@@ -71,8 +71,8 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati
/** Convert sequence argument to Java array */
private def seqToArray(tree: Tree)(implicit ctx: Context): Tree = tree match {
- case SeqLiteral(elems) =>
- JavaSeqLiteral(elems)
+ case SeqLiteral(elems, elemtpt) =>
+ JavaSeqLiteral(elems, elemtpt)
case _ =>
val elemType = tree.tpe.firstBaseArgInfo(defn.SeqClass)
var elemClass = elemType.classSymbol
diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala
index fd556b572..d9445d046 100644
--- a/src/dotty/tools/dotc/transform/ExpandSAMs.scala
+++ b/src/dotty/tools/dotc/transform/ExpandSAMs.scala
@@ -25,13 +25,9 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer =>
import ast.tpd._
- /** Is SAMType `cls` also a SAM under the rules of the JVM? */
- def isJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
- cls.is(NoInitsTrait) &&
- cls.superClass == defn.ObjectClass &&
- cls.directlyInheritedTraits.forall(_.is(NoInits)) &&
- !ExplicitOuter.needsOuterIfReferenced(cls) &&
- cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary
+ /** Is the SAMType `cls` also a SAM under the rules of the platform? */
+ def isPlatformSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ ctx.platform.isSam(cls)
override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol =>
@@ -39,7 +35,7 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer =>
case NoType => tree // it's a plain function
case tpe @ SAMType(_) if tpe.isRef(defn.PartialFunctionClass) =>
toPartialFunction(tree)
- case tpe @ SAMType(_) if isJvmSam(tpe.classSymbol.asClass) =>
+ case tpe @ SAMType(_) if isPlatformSam(tpe.classSymbol.asClass) =>
tree
case tpe =>
val Seq(samDenot) = tpe.abstractTermMembers.filter(!_.symbol.is(SuperAccessor))
diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
index 9170cd277..7ec0739c1 100644
--- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
@@ -326,7 +326,12 @@ object ExplicitOuter {
val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore
ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls")
if (treeCls == toCls) tree
- else loop(tree.select(outerAccessor(treeCls.asClass)(outerAccessorCtx)).ensureApplied)
+ else {
+ val acc = outerAccessor(treeCls.asClass)(outerAccessorCtx)
+ assert(acc.exists,
+ i"failure to construct path from ${ctx.owner.ownersIterator.toList}%/% to `this` of ${toCls.showLocated};\n${treeCls.showLocated} does not have an outer accessor")
+ loop(tree.select(acc).ensureApplied)
+ }
}
ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}")
loop(start)
diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala
index 882e42d2f..75235d0f5 100644
--- a/src/dotty/tools/dotc/transform/Getters.scala
+++ b/src/dotty/tools/dotc/transform/Getters.scala
@@ -68,8 +68,8 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform =>
private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic
override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree =
- if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree
+ if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs).withPos(tree.pos) else tree
override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree =
- if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs) else tree
+ if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs).withPos(tree.pos) else tree
}
diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala
index 25b9afa68..fc02e68cc 100644
--- a/src/dotty/tools/dotc/transform/LazyVals.scala
+++ b/src/dotty/tools/dotc/transform/LazyVals.scala
@@ -12,6 +12,8 @@ import Symbols._
import Decorators._
import NameOps._
import StdNames.nme
+import rewrite.Rewrites.patch
+import util.Positions.Position
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, MiniPhaseTransform}
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.ast.{untpd, tpd}
@@ -65,19 +67,20 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
val sym = tree.symbol
if (!(sym is Flags.Lazy) || sym.owner.is(Flags.Trait) || (sym.isStatic && sym.is(Flags.Module))) tree
else {
-
val isField = sym.owner.isClass
-
if (isField) {
if (sym.isVolatile ||
- (sym.is(Flags.Module) && !sym.is(Flags.Synthetic)))
- // module class is user-defined.
- // Should be threadsafe, to mimic safety guaranteed by global object
+ (sym.is(Flags.Module)/* || ctx.scala2Mode*/) &&
+ // TODO assume @volatile once LazyVals uses static helper constructs instead of
+ // ones in the companion object.
+ !sym.is(Flags.Synthetic))
+ // module class is user-defined.
+ // Should be threadsafe, to mimic safety guaranteed by global object
transformMemberDefVolatile(tree)
- else if (sym.is(Flags.Module)) { // synthetic module
+ else if (sym.is(Flags.Module)) // synthetic module
transformSyntheticModule(tree)
- }
- else transformMemberDefNonVolatile(tree)
+ else
+ transformMemberDefNonVolatile(tree)
}
else transformLocalDef(tree)
}
diff --git a/src/dotty/tools/dotc/transform/SeqLiterals.scala b/src/dotty/tools/dotc/transform/SeqLiterals.scala
index 6e29d7e09..49ea69530 100644
--- a/src/dotty/tools/dotc/transform/SeqLiterals.scala
+++ b/src/dotty/tools/dotc/transform/SeqLiterals.scala
@@ -32,9 +32,9 @@ class SeqLiterals extends MiniPhaseTransform {
override def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
case tree: JavaSeqLiteral => tree
case _ =>
- val arr = JavaSeqLiteral(tree.elems)
+ val arr = JavaSeqLiteral(tree.elems, tree.elemtpt)
//println(i"trans seq $tree, arr = $arr: ${arr.tpe} ${arr.tpe.elemType}")
- val elemtp = arr.tpe.elemType.bounds.hi
+ val elemtp = tree.elemtpt.tpe
val elemCls = elemtp.classSymbol
val (wrapMethStr, targs) =
if (elemCls.isPrimitiveValueClass) (s"wrap${elemCls.name}Array", Nil)
diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala
index 7a2280baa..7fe003388 100644
--- a/src/dotty/tools/dotc/transform/TreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/TreeTransform.scala
@@ -1148,7 +1148,8 @@ object TreeTransforms {
if (mutatedInfo eq null) tree
else {
val elems = transformTrees(tree.elems, mutatedInfo, cur)
- goSeqLiteral(cpy.SeqLiteral(tree)(elems), mutatedInfo.nx.nxTransSeqLiteral(cur))
+ val elemtpt = transform(tree.elemtpt, mutatedInfo, cur)
+ goSeqLiteral(cpy.SeqLiteral(tree)(elems, elemtpt), mutatedInfo.nx.nxTransSeqLiteral(cur))
}
case tree: TypeTree =>
implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur)
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 3b8c56ea6..f3903e539 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -442,7 +442,10 @@ trait Applications extends Compatibility { self: Typer =>
def makeVarArg(n: Int, elemFormal: Type): Unit = {
val args = typedArgBuf.takeRight(n).toList
typedArgBuf.trimEnd(n)
- val seqLit = if (methodType.isJava) JavaSeqLiteral(args) else SeqLiteral(args)
+ val elemtpt = TypeTree(elemFormal)
+ val seqLit =
+ if (methodType.isJava) JavaSeqLiteral(args, elemtpt)
+ else SeqLiteral(args, elemtpt)
typedArgBuf += seqToRepeated(seqLit)
}
@@ -771,7 +774,7 @@ trait Applications extends Compatibility { self: Typer =>
for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show)
val bunchedArgs = argTypes match {
case argType :: Nil =>
- if (argType.isRepeatedParam) untpd.SeqLiteral(args) :: Nil
+ if (argType.isRepeatedParam) untpd.SeqLiteral(args, untpd.TypeTree()) :: Nil
else if (args.lengthCompare(1) > 0 && ctx.canAutoTuple) untpd.Tuple(args) :: Nil
else args
case _ => args
@@ -1089,7 +1092,10 @@ trait Applications extends Compatibility { self: Typer =>
val alts2 = narrowByShapes(alts1)
//ctx.log(i"narrowed by shape: ${alts1.map(_.symbol.showDcl)}%, %")
if (isDetermined(alts2)) alts2
- else narrowByTrees(alts2, pt.typedArgs, resultType)
+ else {
+ pretypeArgs(alts2, pt)
+ narrowByTrees(alts2, pt.typedArgs, resultType)
+ }
}
case pt @ PolyProto(targs, pt1) =>
@@ -1122,6 +1128,69 @@ trait Applications extends Compatibility { self: Typer =>
}
}
+ /** Try to typecheck any arguments in `pt` that are function values missing a
+ * parameter type. The expected type for these arguments is the lub of the
+ * corresponding formal parameter types of all alternatives. Type variables
+ * in formal parameter types are replaced by wildcards. The result of the
+ * typecheck is stored in `pt`, to be retrieved when its `typedArgs` are selected.
+ * The benefit of doing this is to allow idioms like this:
+ *
+ * def map(f: Char => Char): String = ???
+ * def map[U](f: Char => U): Seq[U] = ???
+ * map(x => x.toUpper)
+ *
+ * Without `pretypeArgs` we'd get a "missing parameter type" error for `x`.
+ * With `pretypeArgs`, we use the union of the two formal parameter types
+ * `Char => Char` and `Char => ?` as the expected type of the closure `x => x.toUpper`.
+ * That union is `Char => Char`, so we have an expected parameter type `Char`
+ * for `x`, and the code typechecks.
+ */
+ private def pretypeArgs(alts: List[TermRef], pt: FunProto)(implicit ctx: Context): Unit = {
+ def recur(altFormals: List[List[Type]], args: List[untpd.Tree]): Unit = args match {
+ case arg :: args1 if !altFormals.exists(_.isEmpty) =>
+ def isUnknownParamType(t: untpd.Tree) = t match {
+ case ValDef(_, tpt, _) => tpt.isEmpty
+ case _ => false
+ }
+ arg match {
+ case arg: untpd.Function if arg.args.exists(isUnknownParamType) =>
+ def isUniform[T](xs: List[T])(p: (T, T) => Boolean) = xs.forall(p(_, xs.head))
+ val formalsForArg: List[Type] = altFormals.map(_.head)
+ // For alternatives alt_1, ..., alt_n, test whether formal types for current argument are of the form
+ // (p_1_1, ..., p_m_1) => r_1
+ // ...
+ // (p_1_n, ..., p_m_n) => r_n
+ val decomposedFormalsForArg: List[Option[(List[Type], Type)]] =
+ formalsForArg.map(defn.FunctionOf.unapply)
+ if (decomposedFormalsForArg.forall(_.isDefined)) {
+ val formalParamTypessForArg: List[List[Type]] =
+ decomposedFormalsForArg.map(_.get._1)
+ if (isUniform(formalParamTypessForArg)((x, y) => x.length == y.length)) {
+ val commonParamTypes = formalParamTypessForArg.transpose.map(ps =>
+ // Given definitions above, for i = 1,...,m,
+ // ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column
+ // If all p_i_k's are the same, assume the type as formal parameter
+ // type of the i'th parameter of the closure.
+ if (isUniform(ps)(ctx.typeComparer.isSameTypeWhenFrozen(_, _))) ps.head
+ else WildcardType)
+ val commonFormal = defn.FunctionOf(commonParamTypes, WildcardType)
+ overload.println(i"pretype arg $arg with expected type $commonFormal")
+ pt.typedArg(arg, commonFormal)
+ }
+ }
+ case _ =>
+ }
+ recur(altFormals.map(_.tail), args1)
+ case _ =>
+ }
+ def paramTypes(alt: Type): List[Type] = alt match {
+ case mt: MethodType => mt.paramTypes
+ case mt: PolyType => paramTypes(mt.resultType)
+ case _ => Nil
+ }
+ recur(alts.map(alt => paramTypes(alt.widen)), pt.args)
+ }
+
private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = {
def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match {
case t :: ts1 =>
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 97b47b2bd..740258821 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -179,7 +179,7 @@ object ProtoTypes {
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
else new FunProto(args, resultType, typer)
- def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty
+ def argsAreTyped: Boolean = myTypedArgs.size == args.length
/** The typed arguments. This takes any arguments already typed using
* `typedArg` into account.
diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala
index 49718fd00..225451886 100644
--- a/src/dotty/tools/dotc/typer/ReTyper.scala
+++ b/src/dotty/tools/dotc/typer/ReTyper.scala
@@ -92,7 +92,7 @@ class ReTyper extends Typer {
try super.typedUnadapted(tree, pt)
catch {
case NonFatal(ex) =>
- Printers.transforms.println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}")
+ println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}")
throw ex
}
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 476839ab3..84951fd2b 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -392,14 +392,12 @@ trait TypeAssigner {
if (cases.isEmpty) tree.withType(expr.tpe)
else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes))
- def assignType(tree: untpd.SeqLiteral, elems: List[Tree])(implicit ctx: Context) = tree match {
- case tree: JavaSeqLiteral =>
- tree.withType(defn.ArrayOf(ctx.typeComparer.lub(elems.tpes).widen))
- case _ =>
- val ownType =
- if (ctx.erasedTypes) defn.SeqType
- else defn.SeqType.appliedTo(ctx.typeComparer.lub(elems.tpes).widen)
- tree.withType(ownType)
+ def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = {
+ val ownType = tree match {
+ case tree: JavaSeqLiteral => defn.ArrayOf(elemtpt.tpe)
+ case _ => if (ctx.erasedTypes) defn.SeqType else defn.SeqType.appliedTo(elemtpt.tpe)
+ }
+ tree.withType(ownType)
}
def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) =
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index fdb92a40b..3b8ada2a8 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -33,6 +33,9 @@ import annotation.tailrec
import Implicits._
import util.Stats.{track, record}
import config.Printers._
+import rewrite.Rewrites.patch
+import NavigateAST._
+import transform.SymUtils._
import language.implicitConversions
object Typer {
@@ -836,7 +839,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") {
val proto1 = pt.elemType orElse WildcardType
val elems1 = tree.elems mapconserve (typed(_, proto1))
- assignType(cpy.SeqLiteral(tree)(elems1), elems1)
+ val proto2 = // the computed type of the `elemtpt` field
+ if (!tree.elemtpt.isEmpty) WildcardType
+ else if (isFullyDefined(proto1, ForceDegree.none)) proto1
+ else if (tree.elems.isEmpty && tree.isInstanceOf[Trees.JavaSeqLiteral[_]])
+ defn.ObjectType // generic empty Java varargs are of type Object[]
+ else ctx.typeComparer.lub(elems1.tpes)
+ val elemtpt1 = typed(tree.elemtpt, proto2)
+ assignType(cpy.SeqLiteral(tree)(elems1, elemtpt1), elems1, elemtpt1)
}
def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") {
@@ -951,13 +961,23 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1)
}
- def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = track("typedBind") {
+ def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Tree = track("typedBind") {
val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos)
val body1 = typed(tree.body, pt1)
typr.println(i"typed bind $tree pt = $pt1 bodytpe = ${body1.tpe}")
- val flags = if (tree.isType) BindDefinedType else EmptyFlags
- val sym = ctx.newSymbol(ctx.owner, tree.name, flags, body1.tpe, coord = tree.pos)
- assignType(cpy.Bind(tree)(tree.name, body1), sym)
+ body1 match {
+ case UnApply(fn, Nil, arg :: Nil) if tree.body.isInstanceOf[untpd.Typed] =>
+ // A typed pattern `x @ (_: T)` with an implicit `ctag: ClassTag[T]`
+ // was rewritten to `x @ ctag(_)`.
+ // Rewrite further to `ctag(x @ _)`
+ assert(fn.symbol.owner == defn.ClassTagClass)
+ tpd.cpy.UnApply(body1)(fn, Nil,
+ typed(untpd.Bind(tree.name, arg).withPos(tree.pos), arg.tpe) :: Nil)
+ case _ =>
+ val flags = if (tree.isType) BindDefinedType else EmptyFlags
+ val sym = ctx.newSymbol(ctx.owner, tree.name, flags, body1.tpe, coord = tree.pos)
+ assignType(cpy.Bind(tree)(tree.name, body1), sym)
+ }
}
def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") {
@@ -984,7 +1004,18 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe
case rhs => typedExpr(rhs, tpt1.tpe)
}
- assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
+ val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
+ patchIfLazy(vdef1)
+ vdef1
+ }
+
+ /** Add a @volitile to lazy vals when rewriting from Scala2 */
+ private def patchIfLazy(vdef: ValDef)(implicit ctx: Context): Unit = {
+ val sym = vdef.symbol
+ if (sym.is(Lazy, butNot = Deferred | Module | Synthetic) && !sym.isVolatile &&
+ ctx.scala2Mode && ctx.settings.rewrite.value.isDefined &&
+ !ctx.isAfterTyper)
+ patch(Position(toUntyped(vdef).envelope.start), "@volatile ")
}
def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") {
@@ -1135,13 +1166,17 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
- def typedAsFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = {
+ def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = {
+ val untpd.PostfixOp(qual, nme.WILDCARD) = tree
val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto
- var res = typed(tree, pt1)
+ var res = typed(qual, pt1)
if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) {
def msg = i"not a function: ${res.tpe}; cannot be followed by `_'"
if (ctx.scala2Mode) {
+ // Under -rewrite, patch `x _` to `(() => x)`
ctx.migrationWarning(msg, tree.pos)
+ patch(Position(tree.pos.start), "(() => ")
+ patch(Position(qual.pos.end, tree.pos.end), ")")
res = typed(untpd.Function(Nil, untpd.TypedSplice(res)))
}
else ctx.error(msg, tree.pos)
@@ -1232,7 +1267,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case tree: untpd.Annotated => typedAnnotated(tree, pt)
case tree: untpd.TypedSplice => tree.tree
case tree: untpd.UnApply => typedUnApply(tree, pt)
- case untpd.PostfixOp(tree, nme.WILDCARD) => typedAsFunction(tree, pt)
+ case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt)
case untpd.EmptyTree => tpd.EmptyTree
case _ => typedUnadapted(desugar(tree), pt)
}
diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala
index b257ee192..274218ee3 100644
--- a/src/dotty/tools/dotc/typer/VarianceChecker.scala
+++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala
@@ -6,6 +6,8 @@ import core._
import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._, NameOps._
import Decorators._
import Variances._
+import util.Positions._
+import rewrite.Rewrites.patch
import config.Printers.variances
/** Provides `check` method to check that all top-level definitions
@@ -108,11 +110,13 @@ class VarianceChecker()(implicit ctx: Context) {
}
private object Traverser extends TreeTraverser {
- def checkVariance(sym: Symbol) = Validator.validateDefinition(sym) match {
+ def checkVariance(sym: Symbol, pos: Position) = Validator.validateDefinition(sym) match {
case Some(VarianceError(tvar, required)) =>
def msg = i"${varianceString(tvar.flags)} $tvar occurs in ${varianceString(required)} position in type ${sym.info} of $sym"
- if (ctx.scala2Mode && sym.owner.isConstructor)
+ if (ctx.scala2Mode && sym.owner.isConstructor) {
ctx.migrationWarning(s"According to new variance rules, this is no longer accepted; need to annotate with @uncheckedVariance:\n$msg", sym.pos)
+ patch(Position(pos.end), " @scala.annotation.unchecked.uncheckedVariance") // TODO use an import or shorten if possible
+ }
else ctx.error(msg, sym.pos)
case None =>
}
@@ -128,12 +132,11 @@ class VarianceChecker()(implicit ctx: Context) {
case defn: MemberDef if skip =>
ctx.debuglog(s"Skipping variance check of ${sym.showDcl}")
case tree: TypeDef =>
- checkVariance(sym)
- traverseChildren(tree)
+ checkVariance(sym, tree.envelope)
case tree: ValDef =>
- checkVariance(sym)
+ checkVariance(sym, tree.envelope)
case DefDef(_, tparams, vparamss, _, _) =>
- checkVariance(sym)
+ checkVariance(sym, tree.envelope)
tparams foreach traverse
vparamss foreach (_ foreach traverse)
case Template(_, _, _, body) =>
diff --git a/src/strawman/collections/CollectionStrawMan4.scala b/src/strawman/collections/CollectionStrawMan4.scala
new file mode 100644
index 000000000..9159b1cfc
--- /dev/null
+++ b/src/strawman/collections/CollectionStrawMan4.scala
@@ -0,0 +1,486 @@
+package strawman.collections
+
+import Predef.{augmentString => _, wrapString => _, _}
+import scala.reflect.ClassTag
+
+/** A strawman architecture for new collections. It contains some
+ * example collection classes and methods with the intent to expose
+ * some key issues. It would be good to compare this to other
+ * implementations of the same functionality, to get an idea of the
+ * strengths and weaknesses of different collection architectures.
+ *
+ * For a test file, see tests/run/CollectionTests.scala.
+ */
+object CollectionStrawMan4 {
+
+ /* ------------ Base Traits -------------------------------- */
+
+ /** Iterator can be used only once */
+ trait IterableOnce[+A] {
+ 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 */
+ trait FromIterable[+C[X] <: Iterable[X]] {
+ def fromIterable[B](v: Iterable[B]): C[B]
+ }
+
+ /** Base trait for companion objects of collections */
+ trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterable[C] {
+ def empty[X]: C[X] = fromIterable(View.Empty)
+ def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
+ }
+
+ /** Base trait for sequence collections */
+ trait Seq[+A] extends Iterable[A] with FromIterable[Seq] {
+ def apply(i: Int): A
+ def length: Int
+ }
+
+ trait Builder[-A, +To] {
+ def +=(x: A): this.type
+ def ++=(xs: IterableOnce[A]): Unit = xs.iterator.foreach(+=)
+ def result: To
+ }
+
+ /* ------------ Operations ----------------------------------- */
+
+ /** Operations returning types unrelated to current collection */
+ trait Ops[A] extends Any {
+ def iterator: Iterator[A]
+ def foreach(f: A => Unit): Unit = iterator.foreach(f)
+ def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op)
+ def foldRight[B](z: B)(op: (A, B) => B): B = iterator.foldRight(z)(op)
+ def indexWhere(p: A => Boolean): Int = iterator.indexWhere(p)
+ def isEmpty: Boolean = !iterator.hasNext
+ def head: A = iterator.next
+ }
+
+ /** Transforms returning same collection type */
+ trait MonoTransforms[A, Repr] extends Any {
+ protected def coll: Iterable[A]
+ protected def fromIterable(it: Iterable[A]): Repr
+ def filter(p: A => Boolean): Repr = fromIterable(View.Filter(coll, p))
+ def partition(p: A => Boolean): (Repr, Repr) = {
+ val pn = View.Partition(coll, p)
+ (fromIterable(pn.left), fromIterable(pn.right))
+ }
+ def drop(n: Int): Repr = fromIterable(View.Drop(coll, n))
+ def to[C[X] <: Iterable[X]](fv: FromIterable[C]): C[A] = fv.fromIterable(coll)
+ }
+
+ trait PolyTransforms[A, C[X]] extends Any {
+ protected def coll: Iterable[A]
+ protected def fromIterable[B](it: Iterable[B]): C[B]
+ def map[B](f: A => B): C[B] = fromIterable(View.Map(coll, f))
+ def flatMap[B](f: A => IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f))
+ def ++[B >: A](xs: IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs))
+ def zip[B](xs: IterableOnce[B]): C[(A, B)] = fromIterable(View.Zip(coll, xs))
+ }
+
+ /** Transforms that only apply to Seq */
+ trait MonoTransformsOfSeqs[A, Repr] extends Any with MonoTransforms[A, Repr] {
+ def reverse: Repr = fromIterable(View.Reverse(coll))
+ }
+
+ /** Implementation of Ops for all generic collections */
+ implicit class IterableOps[A](val c: Iterable[A])
+ extends AnyVal with Ops[A] {
+ def iterator = c.iterator
+ }
+
+ /** Implementation of MonoTransforms for all generic collections */
+ implicit class IterableMonoTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterable[C])
+ extends AnyVal with MonoTransforms[A, C[A]] {
+ protected def coll = c
+ protected def fromIterable(it: Iterable[A]): C[A] = c.fromIterable(it)
+ }
+
+ /** Implementation of PolyTransforms for all generic collections */
+ implicit class IterablePolyTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterable[C])
+ extends AnyVal with PolyTransforms[A, C] {
+ protected def coll = c
+ protected def fromIterable[B](it: Iterable[B]): C[B] = c.fromIterable(it)
+ }
+
+ /** Implementation of MonoTransformsForSeqs for all generic collections */
+ implicit class SeqMonoTransforms[A, C[X] <: Seq[X]](val c: Seq[A] with FromIterable[C])
+ extends AnyVal with MonoTransformsOfSeqs[A, C[A]] {
+ protected def coll = c
+ protected def fromIterable(it: Iterable[A]): C[A] = c.fromIterable(it)
+ }
+
+ /* --------- Concrete collection types ------------------------------- */
+
+ /** Concrete collection type: List */
+ sealed trait List[+A] extends Seq[A] with FromIterable[List] { self =>
+ def isEmpty: Boolean
+ def head: A
+ def tail: List[A]
+ def iterator = new Iterator[A] {
+ private[this] var current = self
+ def hasNext = !current.isEmpty
+ def next = { val r = current.head; current = current.tail; r }
+ }
+ def fromIterable[B](c: Iterable[B]): List[B] = List.fromIterable(c)
+ def apply(i: Int): A = {
+ 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
+ }
+
+ case class Cons[+A](x: A, xs: List[A]) extends List[A] {
+ def isEmpty = false
+ def head = x
+ def tail = xs
+ }
+
+ case object Nil extends List[Nothing] {
+ def isEmpty = true
+ def head = ???
+ def tail = ???
+ }
+
+ object List extends IterableFactory[List] {
+ 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.Drop(xs: List[B], n) =>
+ var i = 0
+ var ys = xs
+ while (i < n && !xs.isEmpty) {
+ ys = ys.tail
+ i += 1
+ }
+ ys
+ case _ => fromIterator(c.iterator)
+ }
+ }
+
+ /** Concrete collection type: ArrayBuffer */
+ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
+ extends Seq[A] with FromIterable[ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
+ def this() = this(new Array[AnyRef](16), 0)
+ private var elems: Array[AnyRef] = initElems
+ private var start = 0
+ private var end = initLength
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ def length = end - start
+ override def knownLength = length
+ override def view = new ArrayBufferView(elems, start, end)
+ def iterator = view.iterator
+ def fromIterable[B](it: Iterable[B]): ArrayBuffer[B] =
+ ArrayBuffer.fromIterable(it)
+ def +=(elem: A): this.type = {
+ if (end == elems.length) {
+ if (start > 0) {
+ Array.copy(elems, start, elems, 0, length)
+ end -= start
+ start = 0
+ }
+ else {
+ val newelems = new Array[AnyRef](end * 2)
+ Array.copy(elems, 0, newelems, 0, end)
+ elems = newelems
+ }
+ }
+ elems(end) = elem.asInstanceOf[AnyRef]
+ end += 1
+ this
+ }
+ def result = this
+ def trimStart(n: Int): Unit = start += (n max 0)
+ override def toString = s"ArrayBuffer(${elems.slice(start, end).mkString(", ")})"
+ }
+
+ object ArrayBuffer extends IterableFactory[ArrayBuffer] {
+ def fromIterable[B](c: Iterable[B]): ArrayBuffer[B] = c match {
+ case View.Concat(fst: ArrayBuffer[B], snd: ArrayBuffer[B]) =>
+ val elems = new Array[AnyRef](fst.length + snd.length)
+ Array.copy(fst.elems, fst.start, elems, 0, fst.length)
+ Array.copy(snd.elems, snd.start, elems, fst.length, snd.length)
+ new ArrayBuffer(elems, elems.length)
+ case pd @ View.Partitioned(partition: View.Partition[B]) =>
+ partition.distribute(new ArrayBuffer[B]())
+ pd.forced.get.asInstanceOf[ArrayBuffer[B]]
+ case c if c.knownLength >= 0 =>
+ val elems = new Array[AnyRef](c.knownLength)
+ val it = c.iterator
+ for (i <- 0 until elems.length) elems(i) = it.next().asInstanceOf[AnyRef]
+ new ArrayBuffer[B](elems, elems.length)
+ case _ =>
+ val buf = new ArrayBuffer[B]
+ val it = c.iterator
+ while (it.hasNext) buf += it.next()
+ buf
+ }
+ }
+
+ class ArrayBufferView[A](val elems: Array[AnyRef], val start: Int, val end: Int) extends RandomAccessView[A] {
+ 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
+ }
+
+ implicit class StringMonoTransforms(val s: String)
+ extends AnyVal with MonoTransformsOfSeqs[Char, String] {
+ protected def coll: Iterable[Char] = StringView(s)
+ protected def fromIterable(it: Iterable[Char]): String = {
+ val sb = new StringBuilder
+ for (ch <- it) sb.append(ch)
+ sb.toString
+ }
+ }
+
+ implicit class StringPolyTransforms(val s: String)
+ extends AnyVal with PolyTransforms[Char, Seq] {
+ protected def coll = StringView(s)
+ protected def fromIterable[B](it: Iterable[B]): Seq[B] = List.fromIterable(it)
+ def map(f: Char => Char): String = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def flatMap(f: Char => String) = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def ++(xs: IterableOnce[Char]): String = {
+ val sb = new StringBuilder(s)
+ for (ch <- xs.iterator) sb.append(ch)
+ sb.toString
+ }
+ def ++(xs: String): String = s + xs
+ }
+
+ /* ------------ Views --------------------------------------- */
+
+ /** A lazy iterable */
+ trait View[+A] extends Iterable[A] with FromIterable[View] {
+ override def view = this
+ override def fromIterable[B](c: Iterable[B]) = c match {
+ case c: View[B] => c
+ case _ => View.fromIterator(c.iterator)
+ }
+ }
+
+ /** Iterator defined in terms of indexing a range */
+ trait RandomAccessView[+A] extends View[A] {
+ def start: Int
+ def end: Int
+ def apply(i: Int): A
+ def iterator: Iterator[A] = new Iterator[A] {
+ private var current = start
+ def hasNext = current < end
+ def next: A = {
+ val r = apply(current)
+ current += 1
+ r
+ }
+ }
+ override def knownLength = end - start max 0
+ }
+
+ object View {
+ def fromIterator[A](it: => Iterator[A]): View[A] = new View[A] {
+ def iterator = it
+ }
+ case object Empty extends View[Nothing] {
+ def iterator = Iterator.empty
+ override def knownLength = 0
+ }
+ case class Elems[A](xs: A*) extends View[A] {
+ def iterator = Iterator(xs: _*)
+ override def knownLength = xs.length
+ }
+ case class Filter[A](val underlying: Iterable[A], p: A => Boolean) extends View[A] {
+ def iterator = underlying.iterator.filter(p)
+ }
+ case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
+ val left, right = Partitioned(this)
+ def distribute(bf: => Builder[A, Iterable[A]]) = {
+ val lb, rb = bf
+ val it = underlying.iterator
+ while (it.hasNext) {
+ val x = it.next()
+ (if (p(x)) lb else rb) += x
+ }
+ left.forced = Some(lb.result)
+ right.forced = Some(rb.result)
+ }
+ }
+ case class Partitioned[A](partition: Partition[A]) extends View[A] {
+ private var myForced: Option[Iterable[A]] = None
+ def forced: Option[Iterable[A]] = myForced
+ private[View] def forced_=(x: Option[Iterable[A]]): Unit = myForced = x
+ def underlying = partition.underlying
+ def iterator = forced match {
+ case Some(c) => c.iterator
+ case None =>
+ underlying.iterator.filter(
+ if (this eq partition.left) partition.p else !partition.p(_))
+ }
+ }
+ case class Drop[A](underlying: Iterable[A], n: Int) extends View[A] {
+ def iterator = underlying.iterator.drop(n)
+ override def knownLength =
+ if (underlying.knownLength >= 0) underlying.knownLength - n max 0 else -1
+ }
+ case class Map[A, B](underlying: Iterable[A], f: A => B) extends View[B] {
+ def iterator = underlying.iterator.map(f)
+ override def knownLength = underlying.knownLength
+ }
+ case class FlatMap[A, B](underlying: Iterable[A], f: A => IterableOnce[B]) extends View[B] {
+ def iterator = underlying.iterator.flatMap(f)
+ }
+ case class Concat[A](underlying: Iterable[A], other: IterableOnce[A]) extends View[A] {
+ def iterator = underlying.iterator ++ other
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength + other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ case class Zip[A, B](underlying: Iterable[A], other: IterableOnce[B]) extends View[(A, B)] {
+ def iterator = underlying.iterator.zip(other)
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength min other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ case class Reverse[A](underlying: Iterable[A]) extends View[A] {
+ def iterator = {
+ var xs: List[A] = Nil
+ val it = underlying.iterator
+ while (it.hasNext) xs = Cons(it.next(), xs)
+ xs.iterator
+ }
+ override def knownLength = underlying.knownLength
+ }
+ }
+
+/* ---------- Iterators ---------------------------------------------------*/
+
+ /** A core Iterator class */
+ trait Iterator[+A] extends IterableOnce[A] { self =>
+ def hasNext: Boolean
+ def next(): A
+ def iterator = this
+ def foldLeft[B](z: B)(op: (B, A) => B): B =
+ if (hasNext) foldLeft(op(z, next))(op) else z
+ def foldRight[B](z: B)(op: (A, B) => B): B =
+ if (hasNext) op(next(), foldRight(z)(op)) else z
+ def foreach(f: A => Unit): Unit =
+ while (hasNext) f(next())
+ def indexWhere(p: A => Boolean): Int = {
+ var i = 0
+ while (hasNext) {
+ if (p(next())) return i
+ i += 1
+ }
+ -1
+ }
+ def filter(p: A => Boolean): Iterator[A] = new Iterator[A] {
+ private var hd: A = _
+ private var hdDefined: Boolean = false
+
+ def hasNext: Boolean = hdDefined || {
+ do {
+ if (!self.hasNext) return false
+ hd = self.next()
+ } while (!p(hd))
+ hdDefined = true
+ true
+ }
+
+ def next() =
+ if (hasNext) {
+ hdDefined = false
+ hd
+ }
+ else Iterator.empty.next()
+ }
+
+ def map[B](f: A => B): Iterator[B] = new Iterator[B] {
+ def hasNext = self.hasNext
+ def next() = f(self.next())
+ }
+
+ def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = Iterator.empty
+ private def current = {
+ while (!myCurrent.hasNext && self.hasNext)
+ myCurrent = f(self.next()).iterator
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = self
+ private var first = true
+ private def current = {
+ if (!myCurrent.hasNext && first) {
+ myCurrent = xs.iterator
+ first = false
+ }
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def drop(n: Int): Iterator[A] = {
+ var i = 0
+ while (i < n && hasNext) {
+ next()
+ i += 1
+ }
+ this
+ }
+ def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new Iterator[(A, B)] {
+ val thatIterator = that.iterator
+ def hasNext = self.hasNext && thatIterator.hasNext
+ def next() = (self.next(), thatIterator.next())
+ }
+ }
+
+ object Iterator {
+ val empty: Iterator[Nothing] = new Iterator[Nothing] {
+ def hasNext = false
+ def next = throw new NoSuchElementException("next on empty iterator")
+ }
+ def apply[A](xs: A*): Iterator[A] = new RandomAccessView[A] {
+ val start = 0
+ val end = xs.length
+ def apply(n: Int) = xs(n)
+ }.iterator
+ }
+}
+
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 2d4ed289c..457116feb 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -104,6 +104,8 @@ class tests extends CompilerTest {
@Test def pos_scala2_all = compileFiles(posScala2Dir, scala2mode)
+ @Test def rewrites = compileFile(posScala2Dir, "rewrites", "-rewrite" :: scala2mode)
+
@Test def pos_859 = compileFile(posSpecialDir, "i859", scala2mode)(allowDeepSubtypes)
@Test def new_all = compileFiles(newDir, twice)
diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala
index 678ef74b1..ef2f719fc 100644
--- a/test/test/CompilerTest.scala
+++ b/test/test/CompilerTest.scala
@@ -10,6 +10,7 @@ import scala.reflect.io.{ Path, Directory, File => SFile, AbstractFile }
import scala.tools.partest.nest.{ FileManager, NestUI }
import scala.annotation.tailrec
import java.io.{ RandomAccessFile, File => JFile }
+import dotty.tools.io.PlainFile
import org.junit.Test
@@ -89,7 +90,20 @@ abstract class CompilerTest {
if (!generatePartestFiles || !partestableFile(prefix, fileName, extension, args ++ defaultOptions)) {
if (runTest)
log(s"WARNING: run tests can only be run by partest, JUnit just verifies compilation: $prefix$fileName$extension")
- compileArgs((s"$filePath" :: args).toArray, expErrors)
+ if (args.contains("-rewrite")) {
+ val file = new PlainFile(filePath)
+ val data = file.toByteArray
+ // compile with rewrite
+ compileArgs((filePath :: args).toArray, expErrors)
+ // compile again, check that file now compiles without -language:Scala2
+ val plainArgs = args.filter(arg => arg != "-rewrite" && arg != "-language:Scala2")
+ compileFile(prefix, fileName, plainArgs, extension, runTest)
+ // restore original test file
+ val out = file.output
+ out.write(data)
+ out.close()
+ }
+ else compileArgs((filePath :: args).toArray, expErrors)
} else {
val kind = testKind(prefix, runTest)
log(s"generating partest files for test file: $prefix$fileName$extension of kind $kind")
@@ -191,6 +205,8 @@ abstract class CompilerTest {
}
}
+
+
// ========== HELPERS =============
private def expectedErrors(filePaths: List[String]): List[ErrorsInFile] = if (filePaths.exists(isNegTest(_))) filePaths.map(getErrors(_)) else Nil
diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala
index 44360d657..77aa293d5 100644
--- a/test/test/DeSugarTest.scala
+++ b/test/test/DeSugarTest.scala
@@ -53,8 +53,8 @@ class DeSugarTest extends ParserTest {
cpy.Typed(tree1)(transform(expr), transform(tpt, Type))
case CaseDef(pat, guard, body) =>
cpy.CaseDef(tree1)(transform(pat, Pattern), transform(guard), transform(body))
- case SeqLiteral(elems) =>
- cpy.SeqLiteral(tree1)(transform(elems))
+ case SeqLiteral(elems, elemtpt) =>
+ cpy.SeqLiteral(tree1)(transform(elems), transform(elemtpt))
case UnApply(fun, implicits, patterns) =>
cpy.UnApply(tree1)(transform(fun, Expr), transform(implicits), transform(patterns))
case tree1 @ ValDef(name, tpt, _) =>
diff --git a/tests/neg/classOf.scala b/tests/neg/classOf.scala
new file mode 100644
index 000000000..e13cf71c4
--- /dev/null
+++ b/tests/neg/classOf.scala
@@ -0,0 +1,11 @@
+object Test {
+
+ class C { type I }
+ type A = C
+
+ def f1[T] = classOf[T] // error
+ def f2[T <: String] = classOf[T] // error
+ val x = classOf[Test.type] // error
+ val y = classOf[C { type I = String }] // error
+ val z = classOf[A] // ok
+}
diff --git a/tests/neg/i1059.scala b/tests/neg/i1059.scala
new file mode 100644
index 000000000..1fb89afd0
--- /dev/null
+++ b/tests/neg/i1059.scala
@@ -0,0 +1,10 @@
+object Repeated {
+ val list = List(1, 2, 3)
+
+ list match {
+ case List(_, _, _, _ @ _*) => 0 // error: only allowed in Scala2 mode
+ case List(_, _, _*) => 1 // error: only allowed in Scala2 mode
+ case List(_, _: _*) => 2
+ case Nil => 3
+ }
+}
diff --git a/tests/neg/overloaded.scala b/tests/neg/overloaded.scala
new file mode 100644
index 000000000..ce971ebcf
--- /dev/null
+++ b/tests/neg/overloaded.scala
@@ -0,0 +1,17 @@
+// testing the limits of parameter type inference
+
+object Test {
+ def mapX(f: Char => Char): String = ???
+ def mapX[U](f: U => U): U = ???
+ mapX(x => x) // error: missing parameter type
+
+ def foo(f: Char => Char): Unit = ???
+ def foo(f: Int => Int): String = ???
+ foo(x => x) // error: missing parameter type
+
+ def bar(f: (Char, Char) => Unit): Unit = ???
+ def bar(f: Char => Unit) = ???
+ bar((x, y) => ())
+ bar (x => ())
+
+}
diff --git a/tests/neg/tate.scala b/tests/neg/tate.scala
new file mode 100644
index 000000000..acf7ee7e3
--- /dev/null
+++ b/tests/neg/tate.scala
@@ -0,0 +1,11 @@
+ object unsound {
+ trait Bound[A, B <: A]
+ trait Bind[A] {
+ def bad[B <: A](bound: Bound[A, B], b: B) = b
+ }
+ def coerce[T, U](t: T): U = {
+ lazy val bound: Bound[U, _ >: T] = ??? // error: >: T does not conform to upper bound
+ def bind = new Bind[U] {}
+ bind.bad(bound, t)
+ }
+ }
diff --git a/tests/pending/neg/tate.scala b/tests/pending/neg/tate.scala
new file mode 100644
index 000000000..d626ccd3f
--- /dev/null
+++ b/tests/pending/neg/tate.scala
@@ -0,0 +1,11 @@
+trait Out[+T]
+
+object Test {
+
+ def foo[T <% AnyRef](x: T) = ???
+
+ var x: Out[_ >: String] = ???
+ var y: Out[String] = ???
+ x = y // should give error, but currently masked by covariant alias representation
+ // y = x
+}
diff --git a/tests/pending/pos/lazyvals.scala b/tests/pending/pos/lazyvals.scala
new file mode 100644
index 000000000..93a82cd0c
--- /dev/null
+++ b/tests/pending/pos/lazyvals.scala
@@ -0,0 +1,18 @@
+
+
+trait Iterator {
+
+ def span() = {
+ val self: Int = 33
+ class Leading {
+ def finish(): Unit = println("finished")
+ }
+ val leading = new Leading
+
+ class Trailing {
+ @volatile lazy val it = leading.finish()
+ }
+ val trailing = new Trailing
+ (leading, trailing)
+ }
+}
diff --git a/tests/pickling/hk.scala b/tests/pickling/hk.scala
index 9fdaf94f6..a8f2aa597 100644
--- a/tests/pickling/hk.scala
+++ b/tests/pickling/hk.scala
@@ -31,7 +31,7 @@ object higherKinded {
}
class Ident[-T >: Untyped] extends Tree[T] {
- type ThisType[-U] = Ident[U]
+ type ThisType[-U >: Untyped] = Ident[U]
}
val id = new Ident[Integer]
diff --git a/tests/pos-scala2/i1059.scala b/tests/pos-scala2/i1059.scala
new file mode 100644
index 000000000..cd23e1916
--- /dev/null
+++ b/tests/pos-scala2/i1059.scala
@@ -0,0 +1,10 @@
+object Repeated {
+ val list = List(1, 2, 3)
+
+ list match {
+ case List(_, _, _, _ @ _*) => 0
+ case List(_, _, _*) => 1
+ case List(_, _: _*) => 2
+ case Nil => 3
+ }
+}
diff --git a/tests/pos-scala2/rewrites.scala b/tests/pos-scala2/rewrites.scala
new file mode 100644
index 000000000..3987821f1
--- /dev/null
+++ b/tests/pos-scala2/rewrites.scala
@@ -0,0 +1,36 @@
+trait Test {
+
+ def baz() {}
+
+ def bar()
+
+ def foo() {
+ println("hi")
+ }
+
+ lazy val x: Int
+}
+
+object Test {
+
+ lazy val x = 1
+
+ @deprecated lazy val y = 2
+
+ @deprecated private lazy val z = 2
+
+ lazy val (x1, y1) = (1, 2)
+
+ @deprecated private lazy val (x2, y2) = (1, 2)
+
+ val yy = x1 _
+ val zz: () => Int = yy
+
+}
+
+class Stream[+A] {
+
+ class Inner(x: A) extends Stream[A]
+
+}
+
diff --git a/tests/pos/hk.scala b/tests/pos/hk.scala
index 9fdaf94f6..a8f2aa597 100644
--- a/tests/pos/hk.scala
+++ b/tests/pos/hk.scala
@@ -31,7 +31,7 @@ object higherKinded {
}
class Ident[-T >: Untyped] extends Tree[T] {
- type ThisType[-U] = Ident[U]
+ type ThisType[-U >: Untyped] = Ident[U]
}
val id = new Ident[Integer]
diff --git a/tests/pos/i1174.scala b/tests/pos/i1174.scala
new file mode 100644
index 000000000..5875a38ad
--- /dev/null
+++ b/tests/pos/i1174.scala
@@ -0,0 +1,19 @@
+import scala.reflect.ClassTag
+import scala.util._
+
+object Main {
+ class A
+
+ def constructAs[TTT <: A](implicit ev: ClassTag[TTT]): Try[TTT] = Try {
+ new A()
+ }.flatMap {
+ case ev(inst) =>
+ val b: TTT = inst
+ Success(inst)
+ case inst: TTT =>
+ Success(inst)
+ case _ =>
+ val tag = implicitly[ClassTag[TTT]]
+ Failure(new ClassCastException(s"Failed to construct instance of class ${tag.runtimeClass.getName}"))
+ }
+}
diff --git a/tests/pos/overloaded.scala b/tests/pos/overloaded.scala
index a26b9b859..6a8e72714 100644
--- a/tests/pos/overloaded.scala
+++ b/tests/pos/overloaded.scala
@@ -21,4 +21,30 @@ object overloaded {
val xs = List("a", "b")
xs.mkString
+
+ def map(f: Char => Char): String = ???
+ def map[U](f: Char => U): Seq[U] = ???
+ val r1 = map(x => x.toUpper)
+ val t1: String = r1
+ val r2 = map(x => x.toInt)
+ val t2: Seq[Int] = r2
+
+ def flatMap(f: Char => String): String = ???
+ def flatMap[U](f: Char => Seq[U]): Seq[U] = ???
+ val r3 = flatMap(x => x.toString)
+ val t3: String = r3
+ val r4 = flatMap(x => List(x))
+ val t4: Seq[Char] = r4
+
+ def bar(f: (Char, Char) => Unit): Unit = ???
+ def bar(f: Char => Unit) = ???
+ bar((x, y) => ())
+ bar (x => ())
+
+ def combine(f: (Char, Int) => Int): Int = ???
+ def combine(f: (String, Int) => String): String = ???
+ val r5 = combine((x: Char, y) => x + y)
+ val t5: Int = r5
+ val r6 = combine((x: String, y) => x ++ y.toString)
+ val t6: String = r6
}
diff --git a/tests/run/CollectionTests.scala b/tests/run/CollectionTests.scala
index bcef54761..4696774d1 100644
--- a/tests/run/CollectionTests.scala
+++ b/tests/run/CollectionTests.scala
@@ -3,7 +3,7 @@ import scala.reflect.ClassTag
object Test {
import strawman.collections._
- import CollectionStrawMan1._
+ import CollectionStrawMan4._
def seqOps(xs: Seq[Int]) = {
val x1 = xs.foldLeft("")(_ + _)
@@ -120,15 +120,15 @@ object Test {
val ys7: String = xs7
val xs8 = xs.drop(2)
val ys8: String = xs8
- val xs9 = xs.map((_: Char) + 1) // !!! need a language change to make this work without the : Char
+ val xs9 = xs.map(_ + 1) // !!! need a language change to make this work without the : Char
val ys9: Seq[Int] = xs9
- val xs9a = xs.map((_: Char).toUpper) // !!! need a language change to make this work without the : Char
+ val xs9a = xs.map(_.toUpper) // !!! need a language change to make this work without the : Char
val ys9a: String = xs9a
val xs10 = xs.flatMap((x: Char) => s"$x,$x")
val ys10: String = xs10
val xs11 = xs ++ xs
val ys11: String = xs11
- val xs11a = xs ++ List('x', 'y')
+ val xs11a = xs ++ List('x', 'y') // Cons('x', Cons('y', Nil))
val ys11a: String = xs11a
val xs12 = xs ++ Nil
val ys12: String = xs12