diff options
Diffstat (limited to 'src/compiler')
40 files changed, 364 insertions, 245 deletions
diff --git a/src/compiler/scala/reflect/macros/contexts/Evals.scala b/src/compiler/scala/reflect/macros/contexts/Evals.scala index 84928ddf86..180a998c39 100644 --- a/src/compiler/scala/reflect/macros/contexts/Evals.scala +++ b/src/compiler/scala/reflect/macros/contexts/Evals.scala @@ -12,7 +12,12 @@ trait Evals { private lazy val evalImporter = ru.mkImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }] def eval[T](expr: Expr[T]): T = { - val imported = evalImporter.importTree(expr.tree) - evalToolBox.eval(imported).asInstanceOf[T] + expr.tree match { + case global.Literal(global.Constant(value)) => + value.asInstanceOf[T] + case _ => + val imported = evalImporter.importTree(expr.tree) + evalToolBox.eval(imported).asInstanceOf[T] + } } }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/contexts/Names.scala b/src/compiler/scala/reflect/macros/contexts/Names.scala index c2f14cf0f1..299af40b94 100644 --- a/src/compiler/scala/reflect/macros/contexts/Names.scala +++ b/src/compiler/scala/reflect/macros/contexts/Names.scala @@ -4,7 +4,9 @@ package contexts trait Names { self: Context => - def freshNameCreator = callsiteTyper.context.unit.fresh + import global._ + + def freshNameCreator = globalFreshNameCreator def fresh(): String = freshName() @@ -16,11 +18,25 @@ trait Names { freshName[NameType](name) def freshName(): String = - freshName("fresh$") - - def freshName(name: String): String = - freshNameCreator.newName(name) + freshName(nme.FRESH_PREFIX) + + def freshName(name: String): String = { + // In comparison with the first version of freshName, current "fresh" names + // at least can't clash with legible user-written identifiers and are much less likely to clash with each other. + // It is still not good enough however, because the counter gets reset every time we create a new Global. + // + // This would most certainly cause problems if Scala featured something like introduceTopLevel, + // but even for def macros this can lead to unexpected troubles. Imagine that one Global + // creates a term of an anonymous type with a member featuring a "fresh" name, and then another Global + // imports that term with a wildcard and then generates a "fresh" name of its own. Given unlucky + // circumstances these "fresh" names might end up clashing. + // + // TODO: hopefully SI-7823 will provide an ultimate answer to this problem. + // In the meanwhile I will also keep open the original issue: SI-6879 "c.freshName is broken". + val sortOfUniqueSuffix = freshNameCreator.newName(nme.FRESH_SUFFIX) + name + "$" + sortOfUniqueSuffix + } def freshName[NameType <: Name](name: NameType): NameType = - name.mapName(freshNameCreator.newName(_)).asInstanceOf[NameType] + name.mapName(freshName(_)).asInstanceOf[NameType] }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/contexts/Typers.scala b/src/compiler/scala/reflect/macros/contexts/Typers.scala index 85204d0f1b..cd3db74016 100644 --- a/src/compiler/scala/reflect/macros/contexts/Typers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Typers.scala @@ -49,4 +49,6 @@ trait Typers { def resetAllAttrs(tree: Tree): Tree = universe.resetAllAttrs(universe.duplicateAndKeepPositions(tree)) def resetLocalAttrs(tree: Tree): Tree = universe.resetLocalAttrs(universe.duplicateAndKeepPositions(tree)) + + def untypecheck(tree: Tree): Tree = universe.resetLocalAttrs(universe.duplicateAndKeepPositions(tree)) } diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index 99b968be3b..a90a3a338b 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -60,7 +60,7 @@ trait GenTypes { reifyProduct(tpe) case tpe @ NullaryMethodType(restpe) => reifyProduct(tpe) - case tpe @ AnnotatedType(anns, underlying, selfsym) => + case tpe @ AnnotatedType(anns, underlying) => reifyAnnotatedType(tpe) case _ => reifyToughType(tpe) @@ -162,8 +162,8 @@ trait GenTypes { /** Reify an annotated type, i.e. the one that makes us deal with AnnotationInfos */ private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { - val AnnotatedType(anns, underlying, selfsym) = tpe - mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) + val AnnotatedType(anns, underlying) = tpe + mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying)) } /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index c68b248240..eafecf9462 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -619,7 +619,7 @@ abstract class TreeBrowsers { toDocument(result) :: ")") ) - case AnnotatedType(annots, tp, _) => + case AnnotatedType(annots, tp) => Document.group( Document.nest(4, "AnnotatedType(" :/: annots.mkString("[", ",", "]") :/: @@ -632,7 +632,7 @@ abstract class TreeBrowsers { Document.group("(" :/: symsToDocument(tparams) :/: "), ") :/: toDocument(result) :: ")")) - case global.analyzer.ImportType(expr) => + case ImportType(expr) => "ImportType(" + expr.toString + ")" diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 4ac6672727..a87a04472a 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -37,7 +37,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { NoSymbol newImport NoPosition setFlag SYNTHETIC - setInfo analyzer.ImportType(qual) + setInfo ImportType(qual) ) val importTree = ( Import(qual, selector) diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index cccae0c3a0..24bce0636d 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -194,7 +194,9 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * (bq:) This transformer has mutable state and should be discarded after use */ private class ResetAttrs(localOnly: Boolean, leaveAlone: Tree => Boolean = null, keepLabels: Boolean = false) { - val debug = settings.debug.value + // this used to be based on -Ydebug, but the need for logging in this code is so situational + // that I've reverted to a hard-coded constant here. + val debug = false val trace = scala.tools.nsc.util.trace when debug val locals = util.HashSet[Symbol](8) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index b13d676c65..23a2e0b37f 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -33,7 +33,7 @@ trait ParsersCommon extends ScannersCommon { self => import global.{currentUnit => _, _} def newLiteral(const: Any) = Literal(Constant(const)) - def literalUnit = newLiteral(()) + def literalUnit = gen.mkSyntheticUnit() /** This is now an abstract class, only to work around the optimizer: * methods in traits are never inlined. @@ -133,7 +133,7 @@ self => val global: Global import global._ - case class OpInfo(lhs: Tree, operator: TermName, offset: Offset) { + case class OpInfo(lhs: Tree, operator: TermName, targs: List[Tree], offset: Offset) { def precedence = Precedence(operator.toString) } @@ -787,10 +787,13 @@ self => private def opHead = opstack.head private def headPrecedence = opHead.precedence private def popOpInfo(): OpInfo = try opHead finally opstack = opstack.tail - private def pushOpInfo(top: Tree) { - val opinfo = OpInfo(top, in.name, in.offset) - opstack ::= opinfo + private def pushOpInfo(top: Tree): Unit = { + val name = in.name + val offset = in.offset ident() + val targs = if (in.token == LBRACKET) exprTypeArgs() else Nil + val opinfo = OpInfo(top, name, targs, offset) + opstack ::= opinfo } def checkHeadAssoc(leftAssoc: Boolean) = checkAssoc(opHead.offset, opHead.operator, leftAssoc) @@ -800,6 +803,9 @@ self => ) def finishPostfixOp(start: Int, base: List[OpInfo], opinfo: OpInfo): Tree = { + if (opinfo.targs.nonEmpty) + syntaxError(opinfo.offset, "type application is not allowed for postfix operators") + val od = stripParens(reduceExprStack(base, opinfo.lhs)) makePostfixSelect(start, opinfo.offset, od, opinfo.operator) } @@ -809,7 +815,7 @@ self => val operatorPos: Position = Position.range(rhs.pos.source, offset, offset, offset + operator.length) val pos = lhs.pos union rhs.pos union operatorPos withPoint offset - atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos)) + atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, opinfo.targs)) } def reduceExprStack(base: List[OpInfo], top: Tree): Tree = reduceStack(isExpr = true, base, top) @@ -831,8 +837,14 @@ self => if (samePrecedence) checkHeadAssoc(leftAssoc) - def loop(top: Tree): Tree = - if (canReduce) loop(finishBinaryOp(isExpr, popOpInfo(), top)) else top + def loop(top: Tree): Tree = if (canReduce) { + val info = popOpInfo() + if (!isExpr && info.targs.nonEmpty) { + syntaxError(info.offset, "type application is not allowed in pattern") + info.targs.foreach(_.setType(ErrorType)) + } + loop(finishBinaryOp(isExpr, info, top)) + } else top loop(top) } @@ -1024,7 +1036,11 @@ self => /** Assumed (provisionally) to be TermNames. */ def ident(skipIt: Boolean): Name = ( - if (isIdent) rawIdent().encode + if (isIdent) { + val name = in.name.encode + in.nextToken() + name + } else syntaxErrorOrIncompleteAnd(expectedMsg(IDENTIFIER), skipIt)(nme.ERROR) ) @@ -1892,9 +1908,9 @@ self => def isDelimiter = in.token == RPAREN || in.token == RBRACE def isCommaOrDelimiter = isComma || isDelimiter val (isUnderscore, isStar) = opstack match { - case OpInfo(Ident(nme.WILDCARD), nme.STAR, _) :: _ => (true, true) - case OpInfo(_, nme.STAR, _) :: _ => (false, true) - case _ => (false, false) + case OpInfo(Ident(nme.WILDCARD), nme.STAR, _, _) :: _ => (true, true) + case OpInfo(_, nme.STAR, _, _) :: _ => (false, true) + case _ => (false, false) } def isSeqPatternClose = isUnderscore && isStar && isSequenceOK && isDelimiter val preamble = "bad simple pattern:" diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 32c15b04aa..e8d46704c3 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -475,14 +475,17 @@ trait Scanners extends ScannersCommon { if (token == INTERPOLATIONID) { nextRawChar() if (ch == '\"') { - nextRawChar() - if (ch == '\"') { + val lookahead = lookaheadReader + lookahead.nextChar() + if (lookahead.ch == '\"') { + nextRawChar() // now eat it offset += 3 nextRawChar() getStringPart(multiLine = true) sepRegions = STRINGPART :: sepRegions // indicate string part sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part } else { + nextChar() token = STRINGLIT strVal = "" } @@ -607,10 +610,7 @@ trait Scanners extends ScannersCommon { if (ch == '`') { nextChar() finishNamed(BACKQUOTED_IDENT) - if (name.length == 0) - syntaxError("empty quoted identifier") - else if (name == nme.WILDCARD) - syntaxError("wildcard invalid as backquoted identifier") + if (name.length == 0) syntaxError("empty quoted identifier") } else syntaxError("unclosed quoted identifier") } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index d88470bd5e..525dcffb0c 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -55,7 +55,13 @@ abstract class TreeBuilder { ValDef(Modifiers(PRIVATE), name, tpt, EmptyTree) /** Create tree representing (unencoded) binary operation expression or pattern. */ - def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { + require(isExpr || targs.isEmpty || targs.exists(_.isErroneous), s"Incompatible args to makeBinop: !isExpr but targs=$targs") + + def mkSelection(t: Tree) = { + def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode)) + if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs)) + } def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args val arguments = right match { case Parens(args) => mkNamed(args) @@ -63,12 +69,12 @@ abstract class TreeBuilder { } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { - Apply(atPos(opPos union left.pos) { Select(stripParens(left), op.encode) }, arguments) + Apply(mkSelection(left), arguments) } else { val x = freshTermName() Block( List(ValDef(Modifiers(SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))), - Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) + Apply(mkSelection(right), List(Ident(x)))) } } else { Apply(Ident(op.encode), stripParens(left) :: arguments) diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index d44e7a9312..1332d01dbd 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -793,10 +793,7 @@ abstract class GenICode extends SubComponent { case _ => } ctx1.bb.emit(cm, tree.pos) - - if (sym == ctx1.method.symbol) { - ctx1.method.recursive = true - } + ctx1.method.updateRecursive(sym) generatedType = if (sym.isClassConstructor) UNIT else toTypeKind(sym.info.resultType) diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 4d1d59cd12..267fa15312 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -195,6 +195,10 @@ trait Members { this } + final def updateRecursive(called: Symbol): Unit = { + recursive ||= (called == symbol) + } + def addLocal(l: Local): Local = findOrElse(locals)(_ == l) { locals ::= l ; l } def addParam(p: Local): Unit = diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 633e71a756..a6d0d3b9fa 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -393,7 +393,7 @@ trait TypeKinds { self: ICodes => // if the first two cases exist because they do or as a defensive measure, but // at the time I added it, RefinedTypes were indeed reaching here. case ExistentialType(_, t) => toTypeKind(t) - case AnnotatedType(_, t, _) => toTypeKind(t) + case AnnotatedType(_, t) => toTypeKind(t) case RefinedType(parents, _) => parents map toTypeKind reduceLeft lub // For sure WildcardTypes shouldn't reach here either, but when // debugging such situations this may come in handy. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 4f9f4c9e31..c8845344e9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -88,7 +88,9 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genThrow(expr: Tree): BType = { val thrownKind = tpeTK(expr) - assert(exemplars.get(thrownKind).isSubtypeOf(ThrowableReference)) + // `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable. + // Similarly for scala.Nothing (again, as defined in src/libray-aux). + assert(thrownKind.isNullType || thrownKind.isNothingType || exemplars.get(thrownKind).isSubtypeOf(ThrowableReference)) genLoad(expr, thrownKind) lineNumber(expr) emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level. @@ -824,7 +826,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { /* Generate code that loads args into label parameters. */ def genLoadLabelArguments(args: List[Tree], lblDef: LabelDef, gotoPos: Position) { - assert(args forall { a => !a.hasSymbolField || a.hasSymbolWhich( s => !s.isLabel) }, s"SI-6089 at: $gotoPos") // SI-6089 val aps = { val params: List[Symbol] = lblDef.params.map(_.symbol) @@ -857,12 +858,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } def genLoadModule(tree: Tree): BType = { - // Working around SI-5604. Rather than failing the compile when we see a package here, check if there's a package object. val module = ( if (!tree.symbol.isPackageClass) tree.symbol else tree.symbol.info.member(nme.PACKAGE) match { - case NoSymbol => abort(s"Cannot use package as value: $tree") ; NoSymbol - case s => devWarning("Bug: found package class where package object expected. Converting.") ; s.moduleClass + case NoSymbol => abort(s"SI-5604: Cannot use package as value: $tree") + case s => abort(s"SI-5604: found package class where package object expected: $tree") } ) lineNumber(tree) @@ -871,7 +871,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } def genLoadModule(module: Symbol) { - if (claszSymbol == module.moduleClass && jMethodName != "readResolve") { + def inStaticMethod = methSymbol != null && methSymbol.isStaticMember + if (claszSymbol == module.moduleClass && jMethodName != "readResolve" && !inStaticMethod) { mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) } else { val mbt = symInfoTK(module) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala index 9dcf263f4f..cc3265c5f9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala @@ -661,6 +661,14 @@ abstract class BCodeGlue extends SubComponent { val CT_NOTHING = brefType("scala/Nothing") // TODO needed? val CT_NULL = brefType("scala/Null") // TODO needed? + val srBooleanRef = brefType("scala/runtime/BooleanRef") + val srByteRef = brefType("scala/runtime/ByteRef") + val srCharRef = brefType("scala/runtime/CharRef") + val srIntRef = brefType("scala/runtime/IntRef") + val srLongRef = brefType("scala/runtime/LongRef") + val srFloatRef = brefType("scala/runtime/FloatRef") + val srDoubleRef = brefType("scala/runtime/DoubleRef") + /* Map from type kinds to the Java reference types. * Useful when pushing class literals onto the operand stack (ldc instruction taking a class literal). * @see Predef.classOf diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 64ed094a47..18ccced75e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -440,13 +440,11 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { if (sym0 == definitions.NullClass) return RT_NULL; if (sym0 == definitions.NothingClass) return RT_NOTHING; - // Working around SI-5604. Rather than failing the compile when we see - // a package here, check if there's a package object. val sym = ( if (!sym0.isPackageClass) sym0 else sym0.info.member(nme.PACKAGE) match { - case NoSymbol => abort(s"Cannot use package as value: ${sym0.fullName}") - case s => devWarning("Bug: found package class where package object expected. Converting.") ; s.moduleClass + case NoSymbol => abort(s"SI-5604: Cannot use package as value: ${sym0.fullName}") + case s => abort(s"SI-5604: found package class where package object expected: $s") } ) @@ -518,7 +516,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { // !!! Iulian says types which make no sense after erasure should not reach here, which includes the ExistentialType, AnnotatedType, RefinedType. case ExistentialType(_, t) => toTypeKind(t) // TODO shouldn't get here but the following does: akka-actor/src/main/scala/akka/util/WildcardTree.scala - case AnnotatedType(_, w, _) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here. + case AnnotatedType(_, w) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here. case RefinedType(parents, _) => parents map toTypeKind reduceLeft jvmWiseLUB // For sure WildcardTypes shouldn't reach here either, but when debugging such situations this may come in handy. @@ -561,7 +559,9 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { memberCTK } - exemplar(csym) // side effect city + + exemplar(csym).directMemberClasses = result + result } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 529295389c..c3492b79a9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -46,6 +46,7 @@ abstract class BCodeIdiomatic extends BCodeGlue { val ObjectReference = brefType("java/lang/Object") val AnyRefReference = ObjectReference + val objArrayReference = arrayOf(ObjectReference) val JAVA_LANG_OBJECT = ObjectReference val JAVA_LANG_STRING = brefType("java/lang/String") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index c921d11d00..360ce58ecc 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -460,7 +460,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { } def lineNumber(tree: Tree) { if (!emitLines || !tree.pos.isDefined) return; - val nr = tree.pos.line + val nr = tree.pos.finalPosition.line if (nr != lastEmittedLineNr) { lastEmittedLineNr = nr lastInsn match { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 27bcbb82d4..9ddb7a3ce8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -386,10 +386,6 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { /* Does this tree have a try-catch block? */ def mayCleanStack(tree: Tree): Boolean = tree exists { t => t.isInstanceOf[Try] } - abstract class Cleanup(val value: AnyRef) { } - case class MonitorRelease(v: Symbol) extends Cleanup(v) { } - case class Finalizer(f: Tree) extends Cleanup (f) { } - trait EHClause case class NamelessEH(typeToDrop: BType, caseBody: Tree) extends EHClause case class BoundEH (patSymbol: Symbol, caseBody: Tree) extends EHClause diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index dd2d63ad17..1eca69936a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -24,11 +24,15 @@ abstract class BCodeTypes extends BCodeIdiomatic { // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface) val isCompilingStdLib = !(settings.sourcepath.isDefault) + val srBoxedUnit = brefType("scala/runtime/BoxedUnit") + // special names var StringReference : BType = null var ThrowableReference : BType = null var jlCloneableReference : BType = null // java/lang/Cloneable + var jlNPEReference : BType = null // java/lang/NullPointerException var jioSerializableReference : BType = null // java/io/Serializable + var scalaSerializableReference : BType = null // scala/Serializable var classCastExceptionReference : BType = null // java/lang/ClassCastException /* A map from scala primitive type-symbols to BTypes */ @@ -52,6 +56,8 @@ abstract class BCodeTypes extends BCodeIdiomatic { /* The Object => String overload. */ var String_valueOf: Symbol = null + var ArrayInterfaces: Set[Tracked] = null + // scala.FunctionX and scala.runtim.AbstractFunctionX val FunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1) val AbstractFunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1) @@ -128,15 +134,17 @@ abstract class BCodeTypes extends BCodeIdiomatic { ) } - exemplar(JavaCloneableClass).c - exemplar(JavaSerializableClass).c - exemplar(SerializableClass).c + exemplar(JavaCloneableClass) + exemplar(JavaSerializableClass) + exemplar(SerializableClass) StringReference = exemplar(StringClass).c StringBuilderReference = exemplar(StringBuilderClass).c ThrowableReference = exemplar(ThrowableClass).c jlCloneableReference = exemplar(JavaCloneableClass).c + jlNPEReference = exemplar(NullPointerExceptionClass).c jioSerializableReference = exemplar(JavaSerializableClass).c + scalaSerializableReference = exemplar(SerializableClass).c classCastExceptionReference = exemplar(ClassCastExceptionClass).c /* @@ -203,6 +211,23 @@ abstract class BCodeTypes extends BCodeIdiomatic { * All methods of this class can-multi-thread */ case class Tracked(c: BType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) { + + // not a case-field because we initialize it only for JVM classes we emit. + private var _directMemberClasses: List[BType] = null + + def directMemberClasses: List[BType] = { + assert(_directMemberClasses != null, s"getter directMemberClasses() invoked too early for $c") + _directMemberClasses + } + + def directMemberClasses_=(bs: List[BType]) { + if (_directMemberClasses != null) { + // TODO we enter here when both mirror class and plain class are emitted for the same ModuleClassSymbol. + assert(_directMemberClasses == bs.sortBy(_.off)) + } + _directMemberClasses = bs.sortBy(_.off) + } + /* `isCompilingStdLib` saves the day when compiling: * (1) scala.Nothing (the test `c.isNonSpecial` fails for it) * (2) scala.Boolean (it has null superClass and is not an interface) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 7e1a82a155..eb40e1dbde 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -469,9 +469,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } bytecodeWriter.writeClass(label, jclassName, arr, outF) } catch { - case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") => - // TODO check where ASM throws the equivalent of CodeSizeTooBigException - log("Skipped class "+jclassName+" because it exceeds JVM limits (it's too big or has methods that are too long).") + case e: java.lang.RuntimeException if e != null && (e.getMessage contains "too large!") => + reporter.error(sym.pos, + s"Could not write class $jclassName because it exceeds JVM code size limits. ${e.getMessage}") } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6ec364bcb6..a3114a3d7b 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -73,48 +73,55 @@ trait ScalaSettings extends AbsScalaSettings val nospecialization = BooleanSetting ("-no-specialization", "Ignore @specialize annotations.") val language = MultiStringSetting("-language", "feature", "Enable one or more language features.") + /* + * The previous "-source" option is intended to be used mainly + * though this helper. + */ + lazy val isScala211: Boolean = (source.value >= ScalaVersion("2.11.0")) + /** * -X "Advanced" settings */ - val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") - val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") - val developer = BooleanSetting ("-Xdev", "Indicates user is a developer - issue warnings about anything which seems amiss") - val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") - val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", + val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") + val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") + val developer = BooleanSetting ("-Xdev", "Indicates user is a developer - issue warnings about anything which seems amiss") + val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") + val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", elidable.MINIMUM, None, elidable.byName get _) - val noForwarders = BooleanSetting ("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") - val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "") - val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") - val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") - val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") - val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") - val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") - val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) - val Xmigration = ScalaVersionSetting("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion) - val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") - val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") - val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") - val plugin = MultiStringSetting("-Xplugin", "paths", "Load a plugin from each classpath.") - val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable plugins by name.") - val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") - val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") - val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) - val Xprint = PhasesSetting ("-Xprint", "Print out program after") - val writeICode = PhasesSetting ("-Xprint-icode", "Log internal icode to *.icode files after", "icode") - val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions, as offsets.") - val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option).") - val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option).") - val resident = BooleanSetting ("-Xresident", "Compiler stays resident: read source filenames from standard input.") - val script = StringSetting ("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "") - val mainClass = StringSetting ("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "") - val Xshowcls = StringSetting ("-Xshow-class", "class", "Show internal representation of class.", "") - val Xshowobj = StringSetting ("-Xshow-object", "object", "Show internal representation of object.", "") - val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.") - val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") - val strictInference = BooleanSetting ("-Xstrict-inference", "Don't infer known-unsound types") + val noForwarders = BooleanSetting ("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") + val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "") + val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") + val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") + val logReflectiveCalls = BooleanSetting ("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") + val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") + val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") + val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) + val Xmigration = ScalaVersionSetting ("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion) + val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") + val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") + val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") + val plugin = MultiStringSetting ("-Xplugin", "paths", "Load a plugin from each classpath.") + val disable = MultiStringSetting ("-Xplugin-disable", "plugin", "Disable plugins by name.") + val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") + val require = MultiStringSetting ("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") + val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) + val Xprint = PhasesSetting ("-Xprint", "Print out program after") + val writeICode = PhasesSetting ("-Xprint-icode", "Log internal icode to *.icode files after", "icode") + val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions, as offsets.") + val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option).") + val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option).") + val resident = BooleanSetting ("-Xresident", "Compiler stays resident: read source filenames from standard input.") + val script = StringSetting ("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "") + val mainClass = StringSetting ("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "") + val Xshowcls = StringSetting ("-Xshow-class", "class", "Show internal representation of class.", "") + val Xshowobj = StringSetting ("-Xshow-object", "object", "Show internal representation of object.", "") + val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.") + val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") + val strictInference = BooleanSetting ("-Xstrict-inference", "Don't infer known-unsound types") + val source = ScalaVersionSetting ("-Xsource", "version", "Treat compiler input as Scala source for the specified version, see SI-8126.", ScalaVersion("2.11")) withPostSetHook ( _ => isScala211) val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") - val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") + val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") /** Compatibility stubs for options whose value name did * not previously match the option name. @@ -154,7 +161,6 @@ trait ScalaSettings extends AbsScalaSettings val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.") val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.") val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None) - val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.") val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs in formatted form.") val XshowtreesCompact = BooleanSetting ("-Yshow-trees-compact", "(Requires -Xprint:) Print detailed ASTs in compact form.") diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index 4b9e056df3..c2d0f5ccec 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -64,8 +64,10 @@ abstract class BrowsingLoaders extends GlobalSymbolLoaders { addPackagePrefix(pre) packagePrefix += ("." + name) case Ident(name) => - if (packagePrefix.length != 0) packagePrefix += "." - packagePrefix += name + if (name != nme.EMPTY_PACKAGE_NAME) { // mirrors logic in Namers, see createPackageSymbol + if (packagePrefix.length != 0) packagePrefix += "." + packagePrefix += name + } case _ => throw new MalformedInput(pkg.pos.point, "illegal tree node in package prefix: "+pkg) } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index f704d8ac89..6ca2205881 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -575,23 +575,28 @@ abstract class ICodeReader extends ClassfileParser { case JVM.invokevirtual => val m = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(CALL_METHOD(m, Dynamic)) + method.updateRecursive(m) case JVM.invokeinterface => val m = pool.getMemberSymbol(u2, static = false); size += 4 in.skip(2) code.emit(CALL_METHOD(m, Dynamic)) + // invokeinterface can't be recursive case JVM.invokespecial => val m = pool.getMemberSymbol(u2, static = false); size += 2 val style = if (m.name == nme.CONSTRUCTOR || m.isPrivate) Static(onInstance = true) else SuperCall(m.owner.name) code.emit(CALL_METHOD(m, style)) + method.updateRecursive(m) case JVM.invokestatic => val m = pool.getMemberSymbol(u2, static = true); size += 2 if (isBox(m)) code.emit(BOX(toTypeKind(m.info.paramTypes.head))) else if (isUnbox(m)) code.emit(UNBOX(toTypeKind(m.info.resultType))) - else + else { code.emit(CALL_METHOD(m, Static(onInstance = false))) + method.updateRecursive(m) + } case JVM.invokedynamic => // TODO, this is just a place holder. A real implementation must parse the class constant entry debuglog("Found JVM invokedynamic instructionm, inserting place holder ICode INVOKE_DYNAMIC.") diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index ce3e7b1bb5..90c15bca61 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -238,9 +238,8 @@ abstract class Pickler extends SubComponent { case ExistentialType(tparams, restpe) => putType(restpe) putSymbols(tparams) - case AnnotatedType(_, underlying, selfsym) => + case AnnotatedType(_, underlying) => putType(underlying) - if (settings.selfInAnnots) putSymbol(selfsym) tp.staticAnnotations foreach putAnnotation case _ => throw new FatalError("bad type: " + tp + "(" + tp.getClass + ")") @@ -450,7 +449,7 @@ abstract class Pickler extends SubComponent { case PolyType(tparams, restpe) => writeRef(restpe); writeRefs(tparams) case ExistentialType(tparams, restpe) => writeRef(restpe); writeRefs(tparams) case StaticallyAnnotatedType(annots, tp) => writeRef(tp) ; writeRefs(annots) - case AnnotatedType(_, tp, _) => writeTypeBody(tp) // write the underlying type if there are no static annotations + case AnnotatedType(_, tp) => writeTypeBody(tp) // write the underlying type if there are no static annotations case CompoundType(parents, _, clazz) => writeRef(clazz); writeRefs(parents) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 6732900ef2..4bbfc945f6 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -61,7 +61,7 @@ abstract class Erasure extends AddInterfaces parents foreach traverse case ClassInfoType(parents, _, _) => parents foreach traverse - case AnnotatedType(_, atp, _) => + case AnnotatedType(_, atp) => traverse(atp) case _ => mapOver(tp) @@ -302,7 +302,7 @@ abstract class Erasure extends AddInterfaces boxedSig(parent) case ClassInfoType(parents, _, _) => superSig(parents) - case AnnotatedType(_, atp, _) => + case AnnotatedType(_, atp) => jsig(atp, existentiallyBound, toplevel, primitiveOK) case BoundedWildcardType(bounds) => println("something's wrong: "+sym0+":"+sym0.tpe+" has a bounded wildcard type") diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index c505d9dc5f..3791af1629 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -438,7 +438,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case NullaryMethodType(resTpe) => specializedTypeVars(resTpe) case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: argSyms.map(_.tpe)) case ExistentialType(_, res) => specializedTypeVars(res) - case AnnotatedType(_, tp, _) => specializedTypeVars(tp) + case AnnotatedType(_, tp) => specializedTypeVars(tp) case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil) case RefinedType(parents, _) => parents flatMap specializedTypeVars toSet case _ => immutable.Set.empty @@ -1098,7 +1098,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict) case (_, ThisType(_)) => unify(tp1, tp2.widen, env, strict) case (RefinedType(_, _), RefinedType(_, _)) => env - case (AnnotatedType(_, tp1, _), tp2) => unify(tp2, tp1, env, strict) + case (AnnotatedType(_, tp1), tp2) => unify(tp2, tp1, env, strict) case (ExistentialType(_, res1), _) => unify(tp2, res1, env, strict) case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => unify(List(lo1, hi1), List(lo2, hi2), env, strict) case _ => diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index ef50ae276f..e193cf3de2 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -685,11 +685,13 @@ abstract class UnCurry extends InfoTransform case Packed(param, tempVal) => (param, tempVal) }.unzip - val rhs1 = localTyper.typedPos(rhs.pos) { - // Patch the method body to refer to the temp vals - val rhsSubstituted = rhs.substituteSymbols(packedParams map (_.symbol), tempVals map (_.symbol)) - // The new method body: { val p$1 = p.asInstanceOf[<dependent type>]; ...; <rhsSubstituted> } - Block(tempVals, rhsSubstituted) + val rhs1 = if (tempVals.isEmpty) rhs else { + localTyper.typedPos(rhs.pos) { + // Patch the method body to refer to the temp vals + val rhsSubstituted = rhs.substituteSymbols(packedParams map (_.symbol), tempVals map (_.symbol)) + // The new method body: { val p$1 = p.asInstanceOf[<dependent type>]; ...; <rhsSubstituted> } + Block(tempVals, rhsSubstituted) + } } (allParams :: Nil, rhs1) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 53bc9a2772..598b12b00d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1353,9 +1353,8 @@ trait Contexts { self: Analyzer => override def toString = tree.toString } - case class ImportType(expr: Tree) extends Type { - override def safeToString = "ImportType("+expr+")" - } + type ImportType = global.ImportType + val ImportType = global.ImportType } object ContextMode { diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala index 73572bcae9..1f1ccbe359 100644 --- a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala @@ -180,7 +180,7 @@ trait DestructureTypes { case SuperType(thistp, supertp) => product(tp, this("this", thistp), this("super", supertp)) case ThisType(clazz) => product(tp, wrapAtom(clazz)) case TypeVar(inst, constr) => product(tp, this("inst", inst), typeConstraint(constr)) - case AnnotatedType(annotations, underlying, _) => annotatedType(annotations, underlying) + case AnnotatedType(annotations, underlying) => annotatedType(annotations, underlying) case ExistentialType(tparams, underlying) => polyFunction(tparams, underlying) case PolyType(tparams, restpe) => polyFunction(tparams, restpe) case MethodType(params, restpe) => monoFunction(params, restpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 19fba639e3..91321d4700 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -389,7 +389,7 @@ trait Implicits { private def dominates(dtor: Type, dted: Type): Boolean = { def core(tp: Type): Type = tp.dealiasWiden match { case RefinedType(parents, defs) => intersectionType(parents map core, tp.typeSymbol.owner) - case AnnotatedType(annots, tp, selfsym) => core(tp) + case AnnotatedType(annots, tp) => core(tp) case ExistentialType(tparams, result) => core(result).subst(tparams, tparams map (t => core(t.info.bounds.hi))) case PolyType(tparams, result) => core(result).subst(tparams, tparams map (t => core(t.info.bounds.hi))) case _ => tp @@ -1060,7 +1060,7 @@ trait Implicits { getParts(restpe) case RefinedType(ps, _) => for (p <- ps) getParts(p) - case AnnotatedType(_, t, _) => + case AnnotatedType(_, t) => getParts(t) case ExistentialType(_, t) => getParts(t) @@ -1365,7 +1365,7 @@ trait Implicits { maybeInvalidConversionError("the result type of an implicit conversion must be more specific than AnyRef") result = SearchFailure } - else if (isInvalidConversionSource(pt)) { + else if (settings.isScala211 && isInvalidConversionSource(pt)) { maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion") result = SearchFailure } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 95f2620061..68d724b6fc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -132,11 +132,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans defaultMethodNames.toList.distinct foreach { name => val methods = clazz.info.findMember(name, 0L, requiredFlags = METHOD, stableOnly = false).alternatives - def hasDefaultParam(tpe: Type): Boolean = tpe match { - case MethodType(params, restpe) => (params exists (_.hasDefault)) || hasDefaultParam(restpe) - case _ => false - } - val haveDefaults = methods filter (sym => hasDefaultParam(sym.info) && !nme.isProtectedAccessorName(sym.name)) + val haveDefaults = methods filter (sym => mexists(sym.info.paramss)(_.hasDefault) && !nme.isProtectedAccessorName(sym.name)) if (haveDefaults.lengthCompare(1) > 0) { val owners = haveDefaults map (_.owner) @@ -1431,7 +1427,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkTypeRefBounds(tp: Type, tree: Tree) = { var skipBounds = false tp match { - case AnnotatedType(ann :: Nil, underlying, selfSym) if ann.symbol == UncheckedBoundsClass => + case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass => skipBounds = true underlying case TypeRef(pre, sym, args) => @@ -1474,7 +1470,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } doTypeTraversal(tree) { - case tp @ AnnotatedType(annots, _, _) => + case tp @ AnnotatedType(annots, _) => applyChecks(annots) case tp => } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index b706e1af6b..06796eca8e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -542,7 +542,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case TypeRef(pre, _, _) => isThisType(pre) case SingleType(pre, _) => isThisType(pre) case RefinedType(parents, _) => parents exists isThisType - case AnnotatedType(_, tp, _) => isThisType(tp) + case AnnotatedType(_, tp) => isThisType(tp) case _ => false } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6b5afce993..9776b1e80e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -555,7 +555,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } val qual = typedQualifier { atPos(tree.pos.makeTransparent) { tree match { - case Ident(_) => Ident(nme.PACKAGEkw) + case Ident(_) => Ident(rootMirror.getPackageObjectWithMember(pre, sym)) case Select(qual, _) => Select(qual, nme.PACKAGEkw) case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw) } @@ -1122,7 +1122,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (treeInfo.isMacroApplication(tree)) adapt(unmarkMacroImplRef(tree), mode, pt, original) else tree } else tree.tpe match { - case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) + case atp @ AnnotatedType(_, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) adaptAnnotations(tree, this, mode, pt) case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0) adaptConstant(value) @@ -1793,7 +1793,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val impl2 = finishMethodSynthesis(impl1, clazz, context) - if (mdef.symbol == PredefModule) + if (settings.isScala211 && mdef.symbol == PredefModule) ensurePredefParentsAreInSameSourceFile(impl2) // SI-5954. On second compile of a companion class contained in a package object we end up @@ -3326,6 +3326,28 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) checkNotMacro() + + // SI-8111 transformNamedApplication eagerly shuffles around the application to preserve + // evaluation order. During this process, it calls `changeOwner` on symbols that + // are transplanted underneath synthetic temporary vals. + // + // Here, we keep track of the symbols owned by `context.owner` to enable us to + // rollback, so that we don't end up with "orphaned" symbols. + // + // TODO: Find a better way! + // + // Note that duplicating trees would not be enough to fix this problem, we would also need to + // clone local symbols in the duplicated tree to truly isolate things (in the spirit of BodyDuplicator), + // or, better yet, disentangle the logic in `transformNamedApplication` so that we could + // determine whether names/defaults is viable *before* transforming trees. + def ownerOf(sym: Symbol) = if (sym == null || sym == NoSymbol) NoSymbol else sym.owner + val symsOwnedByContextOwner = tree.collect { + case t @ (_: DefTree | _: Function) if ownerOf(t.symbol) == context.owner => t.symbol + } + def rollbackNamesDefaultsOwnerChanges() { + symsOwnedByContextOwner foreach (_.owner = context.owner) + } + val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) if (fun1.isErroneous) duplErrTree else { @@ -3354,6 +3376,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { + rollbackNamesDefaultsOwnerChanges() tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) } } @@ -3470,7 +3493,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper /** * Convert an annotation constructor call into an AnnotationInfo. */ - def typedAnnotation(ann: Tree, mode: Mode = EXPRmode, selfsym: Symbol = NoSymbol): AnnotationInfo = { + def typedAnnotation(ann: Tree, mode: Mode = EXPRmode): AnnotationInfo = { var hasError: Boolean = false val pending = ListBuffer[AbsTypeError]() @@ -3519,7 +3542,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper reportAnnotationError(ArrayConstantsError(tree)); None case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => - val annInfo = typedAnnotation(ann, mode, NoSymbol) + val annInfo = typedAnnotation(ann, mode) val annType = annInfo.tpe if (!annType.typeSymbol.isSubClass(pt.typeSymbol)) @@ -3631,28 +3654,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } else { - val typedAnn = if (selfsym == NoSymbol) { + val typedAnn: Tree = { // local dummy fixes SI-5544 val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos))) localTyper.typed(ann, mode, annType) } - else { - // Since a selfsym is supplied, the annotation should have an extra - // "self" identifier in scope for type checking. This is implemented - // by wrapping the rhs in a function like "self => rhs" during type - // checking, and then stripping the "self =>" and substituting in - // the supplied selfsym. - val funcparm = ValDef(NoMods, nme.self, TypeTree(selfsym.info), EmptyTree) - // The .duplicate of annot.constr deals with problems that accur - // if this annotation is later typed again, which the compiler - // sometimes does. The problem is that "self" ident's within - // annot.constr will retain the old symbol from the previous typing. - val func = Function(funcparm :: Nil, ann.duplicate) - val funcType = appliedType(FunctionClass(1), selfsym.info, annType) - val Function(arg :: Nil, rhs) = typed(func, mode, funcType) - - rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil) - } def annInfo(t: Tree): AnnotationInfo = t match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => AnnotationInfo(annType, args, List()).setOriginal(typedAnn).setPos(t.pos) @@ -3770,7 +3776,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper t match { case ExistentialType(tparams, _) => boundSyms ++= tparams - case AnnotatedType(annots, _, _) => + case AnnotatedType(annots, _) => for (annot <- annots; arg <- annot.args) { arg match { case Ident(_) => @@ -4035,39 +4041,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (arg1.isType) { // make sure the annotation is only typechecked once if (ann.tpe == null) { - // an annotated type - val selfsym = - if (!settings.selfInAnnots) - NoSymbol - else - arg1.tpe.selfsym orElse { - /* Implementation limitation: Currently this - * can cause cyclical reference errors even - * when the self symbol is not referenced at all. - * Surely at least some of these cases can be - * fixed by proper use of LazyType's. Lex tinkered - * on this but did not succeed, so is leaving - * it alone for now. Example code with the problem: - * class peer extends Annotation - * class NPE[T <: NPE[T] @peer] - * - * (Note: -Yself-in-annots must be on to see the problem) - * */ - ( context.owner - newLocalDummy (ann.pos) - newValue (nme.self, ann.pos) - setInfo (arg1.tpe.withoutAnnotations) - ) - } - - val ainfo = typedAnnotation(ann, annotMode, selfsym) - val atype0 = arg1.tpe.withAnnotation(ainfo) - val atype = - if ((selfsym != NoSymbol) && (ainfo.refsSymbol(selfsym))) - atype0.withSelfsym(selfsym) - else - atype0 // do not record selfsym if - // this annotation did not need it + val ainfo = typedAnnotation(ann, annotMode) + val atype = arg1.tpe.withAnnotation(ainfo) if (ainfo.isErroneous) // Erroneous annotations were already reported in typedAnnotation diff --git a/src/compiler/scala/tools/reflect/ToolBox.scala b/src/compiler/scala/tools/reflect/ToolBox.scala index 236b868842..4c1bc794bc 100644 --- a/src/compiler/scala/tools/reflect/ToolBox.scala +++ b/src/compiler/scala/tools/reflect/ToolBox.scala @@ -72,19 +72,22 @@ trait ToolBox[U <: scala.reflect.api.Universe] { def inferImplicitView(tree: u.Tree, from: u.Type, to: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree /** Recursively resets symbols and types in a given tree. - * - * Note that this does not revert the tree to its pre-typer shape. - * For more info, read up https://issues.scala-lang.org/browse/SI-5464. + * WARNING: Don't use this API, go for [[untypecheck]] instead. */ + @deprecated("Use `tb.untypecheck` instead", "2.11.0") def resetAllAttrs(tree: u.Tree): u.Tree /** Recursively resets locally defined symbols and types in a given tree. - * - * Note that this does not revert the tree to its pre-typer shape. - * For more info, read up https://issues.scala-lang.org/browse/SI-5464. + * WARNING: Don't use this API, go for [[untypecheck]] instead. */ + @deprecated("Use `tb.untypecheck` instead", "2.11.0") def resetLocalAttrs(tree: u.Tree): u.Tree + /** + * @see [[scala.reflect.macros.Typers.untypecheck]] + */ + def untypecheck(tree: u.Tree): u.Tree + /** .. */ def parse(code: String): u.Tree diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index af13b7d0ba..4a8c91bd1b 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -401,6 +401,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => uttree } + def untypecheck(tree: u.Tree): u.Tree = resetLocalAttrs(tree) + def parse(code: String): u.Tree = withCompilerApi { compilerApi => import compilerApi._ if (compiler.settings.verbose) println("parsing "+code) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala index f5bcaf68e0..2027d43264 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala @@ -31,26 +31,28 @@ trait Holes { self: Quasiquotes => import definitions._ import universeTypes._ - protected lazy val IterableTParam = IterableClass.typeParams(0).asType.toType - protected def inferParamImplicit(tfun: Type, targ: Type) = c.inferImplicitValue(appliedType(tfun, List(targ)), silent = true) - protected def inferLiftable(tpe: Type): Tree = inferParamImplicit(liftableType, tpe) - protected def inferUnliftable(tpe: Type): Tree = inferParamImplicit(unliftableType, tpe) - protected def isLiftableType(tpe: Type) = inferLiftable(tpe) != EmptyTree - protected def isNativeType(tpe: Type) = + private lazy val IterableTParam = IterableClass.typeParams(0).asType.toType + private def inferParamImplicit(tfun: Type, targ: Type) = c.inferImplicitValue(appliedType(tfun, List(targ)), silent = true) + private def inferLiftable(tpe: Type): Tree = inferParamImplicit(liftableType, tpe) + private def inferUnliftable(tpe: Type): Tree = inferParamImplicit(unliftableType, tpe) + private def isLiftableType(tpe: Type) = inferLiftable(tpe) != EmptyTree + private def isNativeType(tpe: Type) = (tpe <:< treeType) || (tpe <:< nameType) || (tpe <:< modsType) || (tpe <:< flagsType) || (tpe <:< symbolType) - protected def isBottomType(tpe: Type) = + private def isBottomType(tpe: Type) = tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe - protected def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) = + private def extractIterableTParam(tpe: Type) = + IterableTParam.asSeenFrom(tpe, IterableClass) + private def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) = if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe) else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe) else if (isBottomType(tpe)) (NoDot, tpe) else { - val targ = IterableTParam.asSeenFrom(tpe, IterableClass) + val targ = extractIterableTParam(tpe) val (card, innerTpe) = stripIterable(targ, limit.map { _.pred }) (card.succ, innerTpe) } - protected def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = { + private def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = { if (n == NoDot) tpe else appliedType(IterableClass.toType, List(iterableTypeFromCard(n.pred, tpe))) } @@ -74,8 +76,7 @@ trait Holes { self: Quasiquotes => class ApplyHole(card: Cardinality, splicee: Tree) extends Hole { val (strippedTpe, tpe): (Type, Type) = { - if (stripIterable(splicee.tpe)._1.value < card.value) cantSplice() - val (_, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card)) + val (strippedCard, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card)) if (isBottomType(strippedTpe)) cantSplice() else if (isNativeType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, strippedTpe)) else if (isLiftableType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, treeType)) @@ -88,14 +89,14 @@ trait Holes { self: Quasiquotes => else if (isLiftableType(itpe)) lifted(itpe)(tree) else global.abort("unreachable") if (card == NoDot) inner(strippedTpe)(splicee) - else iterated(card, strippedTpe, inner(strippedTpe))(splicee) + else iterated(card, splicee, splicee.tpe) } val pos = splicee.pos val cardinality = stripIterable(tpe)._1 - protected def cantSplice(): Nothing = { + private def cantSplice(): Nothing = { val (iterableCard, iterableType) = stripIterable(splicee.tpe) val holeCardMsg = if (card != NoDot) s" with $card" else "" val action = "splice " + splicee.tpe + holeCardMsg @@ -111,29 +112,66 @@ trait Holes { self: Quasiquotes => c.abort(splicee.pos, s"Can't $action, $advice") } - protected def lifted(tpe: Type)(tree: Tree): Tree = { + private def lifted(tpe: Type)(tree: Tree): Tree = { val lifter = inferLiftable(tpe) assert(lifter != EmptyTree, s"couldnt find a liftable for $tpe") val lifted = Apply(lifter, List(tree)) - val targetType = Select(u, tpnme.Tree) - atPos(tree.pos)(TypeApply(Select(lifted, nme.asInstanceOf_), List(targetType))) + atPos(tree.pos)(lifted) } - protected def iterated(card: Cardinality, tpe: Type, elementTransform: Tree => Tree = identity)(tree: Tree): Tree = { - assert(card != NoDot) - def reifyIterable(tree: Tree, n: Cardinality): Tree = { - def loop(tree: Tree, n: Cardinality): Tree = - if (n == NoDot) elementTransform(tree) - else { - val x: TermName = c.freshName() - val wrapped = reifyIterable(Ident(x), n.pred) - val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped) - Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList) - } - if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree - else atPos(tree.pos)(loop(tree, n)) + private def toStats(tree: Tree): Tree = + // q"$u.build.toStats($tree)" + Apply(Select(Select(u, nme.build), nme.toStats), tree :: Nil) + + private def toList(tree: Tree, tpe: Type): Tree = + if (isListType(tpe)) tree + else Select(tree, nme.toList) + + private def mapF(tree: Tree, f: Tree => Tree): Tree = + if (f(Ident(TermName("x"))) equalsStructure Ident(TermName("x"))) tree + else { + val x: TermName = c.freshName() + // q"$tree.map { $x => ${f(Ident(x))} }" + Apply(Select(tree, nme.map), + Function(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree) :: Nil, + f(Ident(x))) :: Nil) } - reifyIterable(tree, card) + + private object IterableType { + def unapply(tpe: Type): Option[Type] = + if (isIterableType(tpe)) Some(extractIterableTParam(tpe)) else None + } + + private object LiftedType { + def unapply(tpe: Type): Option[Tree => Tree] = + if (tpe <:< treeType) Some(t => t) + else if (isLiftableType(tpe)) Some(lifted(tpe)(_)) + else None + } + + /** Map high-cardinality splice onto an expression that eveluates as a list of given cardinality. + * + * All possible combinations of representations are given in the table below: + * + * input output for T <: Tree output for T: Liftable + * + * ..${x: Iterable[T]} x.toList x.toList.map(lift) + * ..${x: T} toStats(x) toStats(lift(x)) + * + * ...${x: Iterable[Iterable[T]]} x.toList { _.toList } x.toList.map { _.toList.map(lift) } + * ...${x: Iterable[T]} x.toList.map { toStats(_) } x.toList.map { toStats(lift(_)) } + * ...${x: T} toStats(x).map { toStats(_) } toStats(lift(x)).map { toStats(_) } + * + * For optimization purposes `x.toList` is represented as just `x` if it is statically known that + * x is not just an Iterable[T] but a List[T]. Similarly no mapping is performed if mapping function is + * known to be an identity. + */ + private def iterated(card: Cardinality, tree: Tree, tpe: Type): Tree = (card, tpe) match { + case (DotDot, tpe @ IterableType(LiftedType(lift))) => mapF(toList(tree, tpe), lift) + case (DotDot, LiftedType(lift)) => toStats(lift(tree)) + case (DotDotDot, tpe @ IterableType(inner)) => mapF(toList(tree, tpe), t => iterated(DotDot, t, inner)) + case (DotDotDot, LiftedType(lift)) => mapF(toStats(lift(tree)), toStats) + case _ => global.abort("unreachable") } } @@ -167,16 +205,15 @@ trait Holes { self: Quasiquotes => /** Full support for unliftable implies that it's possible to interleave * deconstruction with higher cardinality and unlifting of the values. * In particular extraction of List[Tree] as List[T: Unliftable] requires - * helper extractors that would do the job: UnliftHelper1[T]. Similarly - * List[List[Tree]] needs UnliftHelper2[T]. + * helper extractors that would do the job: UnliftListElementwise[T]. Similarly + * List[List[Tree]] needs UnliftListOfListsElementwise[T]. * * See also "unlift list" tests in UnapplyProps.scala */ object unlifters { private var records = List.empty[(Type, Cardinality)] - // Request an UnliftHelperN[T] where n == card and T == tpe. - // If card == 0 then helper is not needed and plain instance - // of unliftable is returned. + // Materialize unlift helper that does elementwise + // unlifting for corresponding cardinality and type. def spawn(tpe: Type, card: Cardinality): Option[Tree] = { val unlifter = inferUnliftable(tpe) if (unlifter == EmptyTree) None @@ -191,7 +228,10 @@ trait Holes { self: Quasiquotes => def preamble(): List[Tree] = records.zipWithIndex.map { case ((tpe, card), idx) => val name = TermName(nme.QUASIQUOTE_UNLIFT_HELPER + idx) - val helperName = card match { case DotDot => nme.UnliftHelper1 case DotDotDot => nme.UnliftHelper2 } + val helperName = card match { + case DotDot => nme.UnliftListElementwise + case DotDotDot => nme.UnliftListOfListsElementwise + } val lifter = inferUnliftable(tpe) assert(helperName.isTermName) // q"val $name: $u.build.${helperName.toTypeName} = $u.build.$helperName($lifter)" diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 7604f779d6..fcb8734644 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -55,7 +55,7 @@ trait Parsers { self: Quasiquotes => def isHole(name: Name): Boolean = holeMap.contains(name) - override implicit def fresh: FreshNameCreator = new FreshNameCreator(nme.QUASIQUOTE_PREFIX) + override implicit lazy val fresh: FreshNameCreator = new FreshNameCreator(nme.QUASIQUOTE_PREFIX) override val treeBuilder = new ParserTreeBuilder { override implicit def fresh: FreshNameCreator = parser.fresh @@ -172,17 +172,26 @@ trait Parsers { self: Quasiquotes => } } - object TermParser extends Parser { - def entryPoint = { parser => - parser.templateOrTopStatSeq() match { - case head :: Nil => Block(Nil, head) - case lst => gen.mkTreeOrBlock(lst) - } + /** Wrapper around tree parsed in q"..." quote. Needed to support ..$ splicing on top-level. */ + object Q { + def apply(tree: Tree): Block = Block(Nil, tree).updateAttachment(Q) + def unapply(tree: Tree): Option[Tree] = tree match { + case Block(Nil, contents) if tree.hasAttachment[Q.type] => Some(contents) + case _ => None } } + object TermParser extends Parser { + def entryPoint = parser => Q(gen.mkTreeOrBlock(parser.templateOrTopStatSeq())) + } + object TypeParser extends Parser { - def entryPoint = _.typ() + def entryPoint = { parser => + if (parser.in.token == EOF) + TypeTree() + else + parser.typ() + } } object CaseParser extends Parser { diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala index 7d777ef7d5..3e703924e8 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala @@ -11,7 +11,7 @@ abstract class Quasiquotes extends Parsers val global: c.universe.type = c.universe import c.universe._ - def debug(msg: String): Unit = + def debug(msg: => String): Unit = if (settings.Yquasiquotedebug.value) println(msg) lazy val (universe: Tree, args, parts, parse, reify, method) = c.macroApplication match { @@ -48,7 +48,7 @@ abstract class Quasiquotes extends Parsers val tree = parse(code) debug(s"parsed:\n${showRaw(tree)}\n$tree\n") val reified = reify(tree) - val sreified = + def sreified = reified .toString .replace("scala.reflect.runtime.`package`.universe.build.", "") diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 9c0a036541..273245f7bd 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -190,12 +190,14 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticFunction, args, body) case SyntacticIdent(name, isBackquoted) => reifyBuildCall(nme.SyntacticIdent, name, isBackquoted) - case Block(Nil, Placeholder(Hole(tree, DotDot))) => + case Q(Placeholder(Hole(tree, DotDot))) => mirrorBuildCall(nme.SyntacticBlock, tree) - case Block(Nil, other) => + case Q(other) => reifyTree(other) - case Block(stats, last) => - reifyBuildCall(nme.SyntacticBlock, stats :+ last) + // Syntactic block always matches so we have to be careful + // not to cause infinite recursion. + case block @ SyntacticBlock(stats) if block.isInstanceOf[Block] => + reifyBuildCall(nme.SyntacticBlock, stats) case Try(block, catches, finalizer) => reifyBuildCall(nme.SyntacticTry, block, catches, finalizer) case Match(selector, cases) => |