diff options
Diffstat (limited to 'src/compiler/scala/tools')
19 files changed, 443 insertions, 436 deletions
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index d553d71bf5..c2d62db558 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -19,7 +19,7 @@ import util.Exceptional.unwrap * exec scala "$0" "$@" * !# * Console.println("Hello, world!") - * argv.toList foreach Console.println + * args.toList foreach Console.println * </pre> * <p>And here is a batch file example on Windows XP:</p> * <pre> @@ -29,7 +29,7 @@ import util.Exceptional.unwrap * goto :eof * ::!# * Console.println("Hello, world!") - * argv.toList foreach Console.println + * args.toList foreach Console.println * </pre> * * @author Lex Spoon diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala index c64b18207a..f3def3c80c 100644 --- a/src/compiler/scala/tools/nsc/ast/Printers.scala +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -178,9 +178,9 @@ trait Printers extends scala.reflect.internal.Printers { this: Global => } } - def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymkinds) - def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymkinds) - def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true) + def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymowners, settings.Yshowsymkinds) + def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymowners, settings.Yshowsymkinds) + def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true, true) def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 0a753f157c..3542fe5945 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -413,12 +413,10 @@ self => * * {{{ * object moduleName { - * def main(argv: Array[String]): Unit = { - * val args = argv + * def main(args: Array[String]): Unit = * new AnyRef { * stmts * } - * } * } * }}} */ @@ -433,9 +431,8 @@ self => // def main def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String))) - def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.argv, mainParamType, EmptyTree)) - def mainSetArgv = List(ValDef(NoMods, nme.args, TypeTree(), Ident(nme.argv))) - def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, gen.mkAnonymousNew(stmts))) + def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.args, mainParamType, EmptyTree)) + def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), gen.mkAnonymousNew(stmts)) // object Main def moduleName = newTermName(ScriptRunner scriptMain settings) @@ -2495,7 +2492,7 @@ self => def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = { val trees = { val pat = if (tp.isEmpty) p else Typed(p, tp) setPos (p.pos union tp.pos) - gen.mkPatDef(newmods, pat, rhs) + makePatDef(newmods, pat, rhs) } if (newmods.isDeferred) { trees match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 525dcffb0c..6e5a3f6ef7 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -168,4 +168,6 @@ abstract class TreeBuilder { vparamss ::: List(evidenceParams) } } + + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = gen.mkPatDef(mods, pat, rhs) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 18ccced75e..359e5d6c29 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -583,53 +583,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCAnnotGen extends BCInnerClassGen { - /* - * can-multi-thread - */ - def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { - val ca = new Array[Char](bytes.length) - var idx = 0 - while (idx < bytes.length) { - val b: Byte = bytes(idx) - assert((b & ~0x7f) == 0) - ca(idx) = b.asInstanceOf[Char] - idx += 1 - } - - ca - } - - /* - * can-multi-thread - */ - private def arrEncode(sb: ScalaSigBytes): Array[String] = { - var strs: List[String] = Nil - val bSeven: Array[Byte] = sb.sevenBitsMayBeZero - // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) - var prevOffset = 0 - var offset = 0 - var encLength = 0 - while (offset < bSeven.size) { - val deltaEncLength = (if (bSeven(offset) == 0) 2 else 1) - val newEncLength = encLength.toLong + deltaEncLength - if (newEncLength >= 65535) { - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - encLength = 0 - prevOffset = offset - } else { - encLength += deltaEncLength - offset += 1 - } - } - if (prevOffset < offset) { - assert(offset == bSeven.length) - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - } - assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? - mkArrayReverse(strs) - } + import genASM.{ubytesToCharArray, arrEncode} /* * can-multi-thread @@ -676,7 +630,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { av.visit(name, strEncode(sb)) } else { val arrAnnotV: asm.AnnotationVisitor = av.visitArray(name) - for(arg <- arrEncode(sb)) { arrAnnotV.visit(name, arg) } + for(arg <- genASM.arrEncode(sb)) { arrAnnotV.visit(name, arg) } arrAnnotV.visitEnd() } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. @@ -772,25 +726,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCJGenSigGen { - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - /* - * must-single-thread - */ - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def getCurrentCUnit(): CompilationUnit /* @return @@ -799,61 +734,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { * * must-single-thread */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - - if (!needsGenericSignature(sym)) { return null } - - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) - if (jsOpt.isEmpty) { return null } - - val sig = jsOpt.get - log(sig) // This seems useful enough in the general case. - - def wrap(op: => Unit) = { - try { op; true } - catch { case _: Throwable => false } - } - - if (settings.Xverify) { - // Run the signature parser to catch bogus signatures. - val isValidSignature = wrap { - // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.CheckClassAdapter - if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } - else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } - else { CheckClassAdapter checkClassSignature sig } - } - - if (!isValidSignature) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) - return null - } - } - - if ((settings.check containsName phaseName)) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return null - } - } - - sig - } + def getGenericSignature(sym: Symbol, owner: Symbol): String = genASM.getGenericSignature(sym, owner, getCurrentCUnit()) } // end of trait BCJGenSigGen @@ -906,7 +787,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } - val jgensig = if (m.isDeferred) null else getGenericSignature(m, module); // only add generic signature if method concrete; bug #1745 + val jgensig = genASM.staticForwarderGenericSignature(m, module, getCurrentCUnit()) addRemoteExceptionAnnot(isRemoteClass, hasPublicBitSet(flags), m) val (throws, others) = m.annotations partition (_.symbol == definitions.ThrowsClass) val thrownExceptions: List[String] = getExceptions(throws) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index b03519ce7d..a389816caf 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -20,7 +20,7 @@ import scala.annotation.tailrec * * Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf */ -abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { +abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { self => import global._ import icodes._ import icodes.opcodes._ @@ -803,134 +803,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { annot.args.isEmpty && !annot.matches(DeprecatedAttr) - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def getCurrentCUnit(): CompilationUnit - /** @return - * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). - * - otherwise the signature in question - */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - - if (!needsGenericSignature(sym)) { return null } - - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) - if (jsOpt.isEmpty) { return null } - - val sig = jsOpt.get - log(sig) // This seems useful enough in the general case. - - def wrap(op: => Unit) = { - try { op; true } - catch { case _: Throwable => false } - } - - if (settings.Xverify) { - // Run the signature parser to catch bogus signatures. - val isValidSignature = wrap { - // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.CheckClassAdapter - if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar - else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } - else { CheckClassAdapter checkClassSignature sig } - } - - if(!isValidSignature) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) - return null - } - } - - if ((settings.check containsName phaseName)) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return null - } - } - - sig - } - - def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { - val ca = new Array[Char](bytes.length) - var idx = 0 - while(idx < bytes.length) { - val b: Byte = bytes(idx) - assert((b & ~0x7f) == 0) - ca(idx) = b.asInstanceOf[Char] - idx += 1 - } - - ca - } - - private def arrEncode(sb: ScalaSigBytes): Array[String] = { - var strs: List[String] = Nil - val bSeven: Array[Byte] = sb.sevenBitsMayBeZero - // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) - var prevOffset = 0 - var offset = 0 - var encLength = 0 - while(offset < bSeven.length) { - val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1) - val newEncLength = encLength.toLong + deltaEncLength - if(newEncLength >= 65535) { - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - encLength = 0 - prevOffset = offset - } else { - encLength += deltaEncLength - offset += 1 - } - } - if(prevOffset < offset) { - assert(offset == bSeven.length) - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - } - assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? - strs.reverse.toArray - } - - private def strEncode(sb: ScalaSigBytes): String = { - val ca = ubytesToCharArray(sb.sevenBitsMayBeZero) - new java.lang.String(ca) - // debug val bvA = new asm.ByteVector; bvA.putUTF8(s) - // debug val enc: Array[Byte] = scala.reflect.internal.pickling.ByteCodecs.encode(bytes) - // debug assert(enc(idx) == bvA.getByte(idx + 2)) - // debug assert(bvA.getLength == enc.size + 2) - } + def getGenericSignature(sym: Symbol, owner: Symbol) = self.getGenericSignature(sym, owner, getCurrentCUnit()) def emitArgument(av: asm.AnnotationVisitor, name: String, @@ -1064,7 +939,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } - val jgensig = if (m.isDeferred) null else getGenericSignature(m, module); // only add generic signature if method concrete; bug #1745 + val jgensig = staticForwarderGenericSignature(m, module, getCurrentCUnit()) addRemoteExceptionAnnot(isRemoteClass, hasPublicBitSet(flags), m) val (throws, others) = m.annotations partition (_.symbol == ThrowsClass) val thrownExceptions: List[String] = getExceptions(throws) @@ -3303,4 +3178,147 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } + // @M don't generate java generics sigs for (members of) implementation + // classes, as they are monomorphic (TODO: ok?) + private def needsGenericSignature(sym: Symbol) = !( + // PP: This condition used to include sym.hasExpandedName, but this leads + // to the total loss of generic information if a private member is + // accessed from a closure: both the field and the accessor were generated + // without it. This is particularly bad because the availability of + // generic information could disappear as a consequence of a seemingly + // unrelated change. + settings.Ynogenericsig + || sym.isArtifact + || sym.isLiftedMethod + || sym.isBridge + || (sym.ownerChain exists (_.isImplClass)) + ) + + final def staticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol, unit: CompilationUnit): String = { + if (sym.isDeferred) null // only add generic signature if method concrete; bug #1745 + else { + // SI-3452 Static forwarder generation uses the same erased signature as the method if forwards to. + // By rights, it should use the signature as-seen-from the module class, and add suitable + // primitive and value-class boxing/unboxing. + // But for now, just like we did in mixin, we just avoid writing a wrong generic signature + // (one that doesn't erase to the actual signature). See run/t3452b for a test case. + val memberTpe = enteringErasure(moduleClass.thisType.memberInfo(sym)) + val erasedMemberType = erasure.erasure(sym)(memberTpe) + if (erasedMemberType =:= sym.info) + getGenericSignature(sym, moduleClass, memberTpe, unit) + else null + } + } + + /** @return + * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). + * - otherwise the signature in question + */ + def getGenericSignature(sym: Symbol, owner: Symbol, unit: CompilationUnit): String = { + val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) + getGenericSignature(sym, owner, memberTpe, unit) + } + def getGenericSignature(sym: Symbol, owner: Symbol, memberTpe: Type, unit: CompilationUnit): String = { + if (!needsGenericSignature(sym)) { return null } + + val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) + if (jsOpt.isEmpty) { return null } + + val sig = jsOpt.get + log(sig) // This seems useful enough in the general case. + + def wrap(op: => Unit) = { + try { op; true } + catch { case _: Throwable => false } + } + + if (settings.Xverify) { + // Run the signature parser to catch bogus signatures. + val isValidSignature = wrap { + // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) + import scala.tools.asm.util.CheckClassAdapter + if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar + else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } + else { CheckClassAdapter checkClassSignature sig } + } + + if(!isValidSignature) { + unit.warning(sym.pos, + """|compiler bug: created invalid generic signature for %s in %s + |signature: %s + |if this is reproducible, please report bug at https://issues.scala-lang.org/ + """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) + return null + } + } + + if ((settings.check containsName phaseName)) { + val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) + val bytecodeTpe = owner.thisType.memberInfo(sym) + if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { + unit.warning(sym.pos, + """|compiler bug: created generic signature for %s in %s that does not conform to its erasure + |signature: %s + |original type: %s + |normalized type: %s + |erasure type: %s + |if this is reproducible, please report bug at http://issues.scala-lang.org/ + """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) + return null + } + } + + sig + } + + def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { + val ca = new Array[Char](bytes.length) + var idx = 0 + while(idx < bytes.length) { + val b: Byte = bytes(idx) + assert((b & ~0x7f) == 0) + ca(idx) = b.asInstanceOf[Char] + idx += 1 + } + + ca + } + + final def arrEncode(sb: ScalaSigBytes): Array[String] = { + var strs: List[String] = Nil + val bSeven: Array[Byte] = sb.sevenBitsMayBeZero + // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) + var prevOffset = 0 + var offset = 0 + var encLength = 0 + while(offset < bSeven.length) { + val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1) + val newEncLength = encLength.toLong + deltaEncLength + if(newEncLength >= 65535) { + val ba = bSeven.slice(prevOffset, offset) + strs ::= new java.lang.String(ubytesToCharArray(ba)) + encLength = 0 + prevOffset = offset + } else { + encLength += deltaEncLength + offset += 1 + } + } + if(prevOffset < offset) { + assert(offset == bSeven.length) + val ba = bSeven.slice(prevOffset, offset) + strs ::= new java.lang.String(ubytesToCharArray(ba)) + } + assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? + strs.reverse.toArray + } + + private def strEncode(sb: ScalaSigBytes): String = { + val ca = ubytesToCharArray(sb.sevenBitsMayBeZero) + new java.lang.String(ca) + // debug val bvA = new asm.ByteVector; bvA.putUTF8(s) + // debug val enc: Array[Byte] = scala.reflect.internal.pickling.ByteCodecs.encode(bytes) + // debug assert(enc(idx) == bvA.getByte(idx + 2)) + // debug assert(bvA.getLength == enc.size + 2) + } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index a3114a3d7b..a385a31165 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -168,6 +168,7 @@ trait ScalaSettings extends AbsScalaSettings = BooleanSetting ("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.") val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") + val Yshowsymowners = BooleanSetting ("-Yshow-symowners", "Print owner identifiers next to symbol names.") val skip = PhasesSetting ("-Yskip", "Skip") val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ygenasmp = StringSetting ("-Ygen-asmp", "dir", "Generate a parallel output directory of .asmp files (ie ASM Textifier output).", "") diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 89f9cb4b06..673bc04bd9 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -172,18 +172,23 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // info) as they are seen from the class. We can't use the member that we get from the // implementation class, as it's a clone that was made after erasure, and thus it does not // know its info at the beginning of erasure anymore. - // Optimize: no need if mixinClass has no typeparams. - mixinMember cloneSymbol clazz modifyInfo (info => - if (mixinClass.typeParams.isEmpty) info - else (clazz.thisType baseType mixinClass) memberInfo mixinMember - ) + val sym = mixinMember cloneSymbol clazz + + val erasureMap = erasure.erasure(mixinMember) + val erasedInterfaceInfo: Type = erasureMap(mixinMember.info) + val specificForwardInfo = (clazz.thisType baseType mixinClass) memberInfo mixinMember + val forwarderInfo = + if (erasureMap(specificForwardInfo) =:= erasedInterfaceInfo) + specificForwardInfo + else { + erasedInterfaceInfo + } + // Optimize: no need if mixinClass has no typeparams. + // !!! JZ Really? What about the effect of abstract types, prefix? + if (mixinClass.typeParams.isEmpty) sym + else sym modifyInfo (_ => forwarderInfo) } - // clone before erasure got rid of type info we'll need to generate a javaSig - // now we'll have the type info at (the beginning of) erasure in our history, - // and now newSym has the info that's been transformed to fit this period - // (no need for asSeenFrom as phase.erasedTypes) - // TODO: verify we need the updateInfo and document why - newSym updateInfo (mixinMember.info cloneInfo newSym) + newSym } /** Add getters and setters for all non-module fields of an implementation diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2043eb5d5d..40b97394f2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -501,10 +501,6 @@ trait ContextErrors { } // doTypeApply - //tryNamesDefaults - def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) = - NormalTypeError(tree, "macro applications do not support named and/or default arguments") - def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) @@ -603,12 +599,11 @@ trait ContextErrors { //adapt def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { + val errorExplanation = "missing arguments for " + meth.fullLocationString + val suggestPartialApplication = ";\nfollow this method with `_' if you want to treat it as a partially applied function" val message = - if (meth.isMacro) MacroTooFewArgumentListsMessage - else "missing arguments for " + meth.fullLocationString + ( - if (meth.isConstructor) "" - else ";\nfollow this method with `_' if you want to treat it as a partially applied function" - ) + if (meth.isMacro || meth.isConstructor) errorExplanation + else errorExplanation + suggestPartialApplication issueNormalTypeError(tree, message) setError(tree) } @@ -748,15 +743,12 @@ trait ContextErrors { throw MacroExpansionException } - private def MacroTooFewArgumentListsMessage = "too few argument lists for macro invocation" - def MacroTooFewArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooFewArgumentListsMessage) - - private def MacroTooManyArgumentListsMessage = "too many argument lists for macro invocation" - def MacroTooManyArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooManyArgumentListsMessage) - - def MacroTooFewArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too few arguments for macro invocation") - - def MacroTooManyArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too many arguments for macro invocation") + def MacroFastTrackFailed(expandee: Tree) = { + // here we speculate that the reason why FastTrackEntry.validate failed is the lack arguments for a given method + // that's not ideal, but on the other hand this allows us to keep FastTrack simple without hooking errorgen into it + MissingArgsForMethodTpeError(expandee, expandee.symbol) + throw MacroExpansionException + } def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = { // errors have been reported by the macro itself, so we do nothing here diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 677c94e063..1f90dd4939 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -353,7 +353,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { new { val universe: self.global.type = self.global val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] - val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse duplicateAndKeepPositions(expandeeTree) + val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).desugared orElse duplicateAndKeepPositions(expandeeTree) } with UnaffiliatedMacroContext { val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */) @@ -371,7 +371,15 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { def standardMacroArgs(typer: Typer, expandee: Tree): MacroArgs = { val macroDef = expandee.symbol val paramss = macroDef.paramss - val treeInfo.Applied(core, targs, argss) = expandee + val treeInfo.Applied(core, targs, maybeNamedArgss) = expandee + val argss = map2(maybeNamedArgss, paramss)((args, params) => { + if (args.exists(_.isInstanceOf[AssignOrNamedArg])) { + val sorted = ListBuffer.fill(params.length)(EmptyTree: Tree) + args foreach { case AssignOrNamedArg(Ident(name), arg) => sorted(params.indexWhere(_.name == name)) = arg } + sorted.toList + } else if (params.length == args.length) args + else args ++ List.fill(params.length - args.length)(EmptyTree) + }) val prefix = core match { case Select(qual, _) => qual; case _ => EmptyTree } val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefix, expandee)) @@ -383,16 +391,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { |paramss: $paramss """.trim) - import typer.TyperErrorGen._ - val isNullaryArgsEmptyParams = argss.isEmpty && paramss == ListOfNil - if (paramss.length < argss.length) MacroTooManyArgumentListsError(expandee) - if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentListsError(expandee) - val macroImplArgs: List[Any] = if (fastTrack contains macroDef) { // Take a dry run of the fast track implementation if (fastTrack(macroDef) validate expandee) argss.flatten - else MacroTooFewArgumentListsError(expandee) + else typer.TyperErrorGen.MacroFastTrackFailed(expandee) } else { def calculateMacroArgs(binding: MacroImplBinding) = { @@ -403,14 +406,6 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { // wrap argss in c.Expr if necessary (i.e. if corresponding macro impl param is of type c.Expr[T]) // expand varargs (nb! varargs can apply to any parameter section, not necessarily to the last one) val trees = map3(argss, paramss, signature)((args, defParams, implParams) => { - val isVarargs = isVarArgsList(defParams) - if (isVarargs) { - if (defParams.length > args.length + 1) MacroTooFewArgumentsError(expandee) - } else { - if (defParams.length < args.length) MacroTooManyArgumentsError(expandee) - if (defParams.length > args.length) MacroTooFewArgumentsError(expandee) - } - val wrappedArgs = mapWithIndex(args)((arg, j) => { val fingerprint = implParams(min(j, implParams.length - 1)) fingerprint match { @@ -421,7 +416,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } }) - if (isVarargs) { + if (isVarArgsList(defParams)) { val (normal, varargs) = wrappedArgs splitAt (defParams.length - 1) normal :+ varargs // pack all varargs into a single Seq argument (varargs Scala style) } else wrappedArgs @@ -529,9 +524,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * the expandee with an error marker set if there has been an error */ abstract class MacroExpander(val typer: Typer, val expandee: Tree) { + val symbol = expandee match { case Block(_, expr) => expr.symbol; case tree => tree.symbol } + def onSuccess(expanded: Tree): Tree def onFallback(expanded: Tree): Tree - def onSuppressed(expandee: Tree): Tree = expandee + def onSuppressed(expanded: Tree): Tree = expanded def onDelayed(expanded: Tree): Tree = expanded def onSkipped(expanded: Tree): Tree = expanded def onFailure(expanded: Tree): Tree = { typer.infer.setError(expandee); expandee } @@ -551,15 +548,15 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) try { withInfoLevel(nodePrinters.InfoLevel.Quiet) { // verbose printing might cause recursive macro expansions - if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { - val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" + if (symbol.isErroneous || (expandee exists (_.isErroneous)) || (desugared exists (_.isErroneous))) { + val reason = if (symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee") onFailure(typer.infer.setError(expandee)) } else try { val expanded = { - val runtime = macroRuntime(expandee) - if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) - else macroExpandWithoutRuntime(typer, expandee) + val runtime = macroRuntime(desugared) + if (runtime != null) macroExpandWithRuntime(typer, desugared, runtime) + else macroExpandWithoutRuntime(typer, desugared) } expanded match { case Success(expanded) => @@ -591,7 +588,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { extends MacroExpander(typer, expandee) { lazy val innerPt = { val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe - if (isBlackbox(expandee)) tp + if (isBlackbox(symbol)) tp else { // approximation is necessary for whitebox macros to guide type inference // read more in the comments for onDelayed below @@ -599,6 +596,50 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { deriveTypeWithWildcards(undetparams)(tp) } } + override protected def expand(desugared: Tree) = { + // SI-5940 in order for a macro expansion that involves named or default arguments + // to see the actual prefix and arguments being passed by the user instead of their desugarings + // we need to inline synthetics in case when `fun` is actually a macro + // underlying macro implementation is going to get explicitly passed arguments in correct order + // and the rest (defaults filled in by the vanilla part of `tryNamesDefaults`) will become empty trees + // in order for the macro to be able to account for evaluation order, the original is provided in `c.macroApplication` + // of course, ideally we would like to provide the impl with right-hand sides of those default arguments + // but currently that is flat out impossible because of the difference in scopes + // anyway this is already an improvement over the former status quo when named/default invocations were outright prohibited + def undoNamesDefaults(tree: Tree): Tree = { + val (qualsym, qual, vdefs0, app @ Applied(_, _, argss)) = tree match { + case Block((qualdef @ ValDef(_, name, _, qual)) +: vdefs, app) if name.startsWith(nme.QUAL_PREFIX) => (qualdef.symbol, qual, vdefs, app) + case Block(vdefs, app) => (NoSymbol, EmptyTree, vdefs, app) + case tree => (NoSymbol, EmptyTree, Nil, tree) + } + val vdefs = vdefs0.map{ case vdef: ValDef => vdef } + def hasNamesDefaults(args: List[Tree]) = { + args.exists(arg => isDefaultGetter(arg) || vdefs.exists(_.symbol == arg.symbol)) + } + def undoNamesDefaults(args: List[Tree], depth: Int) = { + def extractRhs(vdef: ValDef) = vdef.rhs.changeOwner(vdef.symbol -> typer.context.owner) + case class Arg(tree: Tree, ipos: Int, inamed: Int) { val param = app.symbol.paramss(depth)(ipos) } + val indexed = args.map(arg => arg -> vdefs.indexWhere(_.symbol == arg.symbol)).zipWithIndex.flatMap({ + /* default */ case ((arg, _), _) if isDefaultGetter(arg) => None + /* positional */ case ((arg, -1), ipos) => Some(Arg(arg, ipos, -1)) + /* default+named */ case ((_, inamed), _) if isDefaultGetter(extractRhs(vdefs(inamed))) => None + /* named */ case ((arg, inamed), ipos) => Some(Arg(extractRhs(vdefs(inamed)), ipos, inamed)) + }) + if (indexed.forall(_.inamed == -1)) indexed.map(_.tree) + else indexed.sortBy(_.inamed).map(arg => AssignOrNamedArg(Ident(arg.param.name), arg.tree)) + } + def loop(tree: Tree, depth: Int): Tree = tree match { + case Apply(fun, args) if hasNamesDefaults(args) => treeCopy.Apply(tree, loop(fun, depth - 1), undoNamesDefaults(args, depth)) + case Apply(fun, args) => treeCopy.Apply(tree, loop(fun, depth - 1), args) + case TypeApply(core, targs) => treeCopy.TypeApply(tree, core, targs) + case Select(core, name) if qualsym != NoSymbol && core.symbol == qualsym => treeCopy.Select(tree, qual, name) + case core => core + } + if (app.symbol == null || app.symbol == NoSymbol || app.exists(_.isErroneous)) tree + else loop(app, depth = argss.length - 1) + } + super.expand(undoNamesDefaults(desugared)) + } override def onSuccess(expanded0: Tree) = { // prematurely annotate the tree with a macro expansion attachment // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup @@ -616,7 +657,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } } - if (isBlackbox(expandee)) { + if (isBlackbox(symbol)) { val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt))) typecheck("blackbox typecheck", expanded1, outerPt) } else { @@ -678,7 +719,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { // Thanks to that the materializer can take a look at what's going on and react accordingly. val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode if (shouldInstantiate) { - if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt) + if (isBlackbox(symbol)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt) else { forced += delayed typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 6a4df415ae..dceb0a47d8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -162,7 +162,7 @@ trait NamesDefaults { self: Analyzer => // never used for constructor calls, they always have a stable qualifier def blockWithQualifier(qual: Tree, selected: Name) = { - val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent) + val sym = blockTyper.context.owner.newValue(unit.freshTermName(nme.QUAL_PREFIX), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent) blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 @@ -292,7 +292,7 @@ trait NamesDefaults { self: Analyzer => arg.tpe } ).widen // have to widen or types inferred from literal defaults will be singletons - val s = context.owner.newValue(unit.freshTermName(), arg.pos, newFlags = ARTIFACT) setInfo { + val s = context.owner.newValue(unit.freshTermName(nme.NAMEDARG_PREFIX), arg.pos, newFlags = ARTIFACT) setInfo { val tp = if (byName) functionType(Nil, argTpe) else argTpe uncheckedBounds(tp) } diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 57f27a05fd..1a6d2f0011 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -85,6 +85,7 @@ trait StdAttachments { tree match { // see the comment to `isMacroExpansionSuppressed` to learn why we need // a special traversal strategy here + case Block(_, expr) => unsuppressMacroExpansion(expr) case Apply(fn, _) => unsuppressMacroExpansion(fn) case TypeApply(fn, _) => unsuppressMacroExpansion(fn) case _ => // do nothing @@ -101,6 +102,8 @@ trait StdAttachments { // we have to account for the fact that during typechecking an expandee might become wrapped, // i.e. surrounded by an inferred implicit argument application or by an inferred type argument application. // in that case the expandee itself will no longer be suppressed and we need to look at the core + // upd. we also need to allow for blocks, because that's what names and defaults are often desugared to + case Block(_, expr) => isMacroExpansionSuppressed(expr) case Apply(fn, _) => isMacroExpansionSuppressed(fn) case TypeApply(fn, _) => isMacroExpansionSuppressed(fn) case _ => false diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 4e3e00b66a..ca960d7b11 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -525,7 +525,6 @@ trait TypeDiagnostics { val unused = p.unusedTerms unused foreach { defn: DefTree => val sym = defn.symbol - val isDefaultGetter = sym.name containsName nme.DEFAULT_GETTER_STRING val pos = ( if (defn.pos.isDefined) defn.pos else if (sym.pos.isDefined) sym.pos @@ -536,7 +535,7 @@ trait TypeDiagnostics { ) val why = if (sym.isPrivate) "private" else "local" val what = ( - if (isDefaultGetter) "default argument" + if (sym.isDefaultGetter) "default argument" else if (sym.isConstructor) "constructor" else if (sym.isVar || sym.isGetter && sym.accessed.isVar) "var" else if (sym.isVal || sym.isGetter && sym.accessed.isVal) "val" diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ea8ad0bf42..ba4b3499e5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -56,6 +56,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } sealed abstract class SilentResult[+T] { + def isEmpty: Boolean + def nonEmpty = !isEmpty + @inline final def fold[U](none: => U)(f: T => U): U = this match { case SilentResultValue(value) => f(value) case _ => none @@ -74,6 +77,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } class SilentTypeError private(val errors: List[AbsTypeError]) extends SilentResult[Nothing] { + override def isEmpty = true def err: AbsTypeError = errors.head def reportableErrors = errors match { case (e1: AmbiguousImplicitTypeError) +: _ => @@ -87,7 +91,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption } - case class SilentResultValue[+T](value: T) extends SilentResult[T] { } + case class SilentResultValue[+T](value: T) extends SilentResult[T] { override def isEmpty = false } def newTyper(context: Context): Typer = new NormalTyper(context) @@ -1157,7 +1161,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case mt: MethodType if mode.typingExprNotFunNotLhs && mt.isImplicit => // (4.1) adaptToImplicitMethod(mt) - case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode && !treeInfo.isMacroApplicationOrBlock(tree) => + case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode => instantiateToMethodType(mt) case _ => vanillaAdapt(tree) @@ -3268,12 +3272,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper */ def tryNamesDefaults: Tree = { val lencmp = compareLengths(args, formals) - - def checkNotMacro() = { - if (treeInfo.isMacroApplication(fun)) - tryTupleApply orElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun)) - } - if (mt.isErroneous) duplErrTree else if (mode.inPatternMode) { // #2064 @@ -3292,18 +3290,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else if (allArgsArePositional(argPos) && !isNamedApplyBlock(fun)) { // if there's no re-ordering, and fun is not transformed, no need to transform // more than an optimization, e.g. important in "synchronized { x = update-x }" - checkNotMacro() doTypedApply(tree, fun, namelessArgs, mode, pt) } else { - checkNotMacro() - transformNamedApplication(Typer.this, mode, pt)( - treeCopy.Apply(tree, fun, namelessArgs), argPos) + unsuppressMacroExpansion(transformNamedApplication(Typer.this, mode, pt)( + treeCopy.Apply(tree, suppressMacroExpansion(fun), namelessArgs), argPos)) } } else { // defaults are needed. they are added to the argument list in named style as // 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 @@ -3326,7 +3321,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper symsOwnedByContextOwner foreach (_.owner = context.owner) } - val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) + val fun1 = transformNamedApplication(Typer.this, mode, pt)(suppressMacroExpansion(fun), x => x) if (fun1.isErroneous) duplErrTree else { assert(isNamedApplyBlock(fun1), fun1) @@ -3352,7 +3347,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() val note = "Error occurred in an application involving default arguments." if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic - doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) + unsuppressMacroExpansion(doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)) } else { rollbackNamesDefaultsOwnerChanges() tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala index d5e674ebae..0258002850 100644 --- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala +++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala @@ -81,7 +81,56 @@ abstract class FormatInterpolator { case Literal(Constant(x: String)) => x case _ => throw new IllegalArgumentException("internal error: argument parts must be a list of string literals") } - val s = StringContext.treatEscapes(s0) + def escapeHatch: PartialFunction[Throwable, String] = { + // trailing backslash, octal escape, or other + case e: StringContext.InvalidEscapeException => + def errPoint = part.pos withPoint (part.pos.point + e.index) + def octalOf(c: Char) = Character.digit(c, 8) + def alt = { + def altOf(i: Int) = i match { + case '\b' => "\\b" + case '\t' => "\\t" + case '\n' => "\\n" + case '\f' => "\\f" + case '\r' => "\\r" + case '\"' => "\\u0022" // $" in future + case '\'' => "'" + case '\\' => """\\""" + case x => "\\u%04x" format x + } + val suggest = { + val r = "([0-7]{1,3}).*".r + (s0 drop e.index + 1) match { + case r(n) => altOf { (0 /: n) { case (a, o) => (8 * a) + (o - '0') } } + case _ => "" + } + } + val txt = + if ("" == suggest) "" + else s", use $suggest instead" + txt + } + def badOctal = { + def msg(what: String) = s"Octal escape literals are $what$alt." + if (settings.future) { + c.error(errPoint, msg("unsupported")) + s0 + } else { + c.enclosingUnit.deprecationWarning(errPoint, msg("deprecated")) + try StringContext.treatEscapes(s0) catch escapeHatch + } + } + if (e.index == s0.length - 1) { + c.error(errPoint, """Trailing '\' escapes nothing.""") + s0 + } else if (octalOf(s0(e.index + 1)) >= 0) { + badOctal + } else { + c.error(errPoint, e.getMessage) + s0 + } + } + val s = try StringContext.processEscapes(s0) catch escapeHatch val ms = fpat findAllMatchIn s def errorLeading(op: Conversion) = op.errorAt(Spec, s"conversions must follow a splice; ${Conversion.literalHelp}") diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3a40b44c74..3b12086cc7 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -64,7 +64,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lastSeenContext = null } - def verify(expr: Tree): Unit = { + def verify(expr: Tree): Tree = { // Previously toolboxes used to typecheck their inputs before compiling. // Actually, the initial demo by Martin first typechecked the reified tree, // then ran it, which typechecked it again, and only then launched the @@ -86,14 +86,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => |if you have troubles tracking free type variables, consider using -Xlog-free-types """.stripMargin.trim) } - } - - def wrapIntoTerm(tree: Tree): Tree = - if (!tree.isTerm) Block(List(tree), Literal(Constant(()))) else tree - def unwrapFromTerm(tree: Tree): Tree = tree match { - case Block(List(tree), Literal(Constant(()))) => tree - case tree => tree + expr } def extractFreeTerms(expr0: Tree, wrapFreeTermRefs: Boolean): (Tree, scala.collection.mutable.LinkedHashMap[FreeTermSymbol, TermName]) = { @@ -122,59 +116,58 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => (expr, freeTermNames) } - def transformDuringTyper(expr0: Tree, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { - verify(expr0) - - // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars - val exprAndFreeTerms = extractFreeTerms(expr0, wrapFreeTermRefs = false) - var expr = exprAndFreeTerms._1 - val freeTerms = exprAndFreeTerms._2 - val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList - expr = Block(dummies, wrapIntoTerm(expr)) - - // [Eugene] how can we implement that? - // !!! Why is this is in the empty package? If it's only to make - // it inaccessible then please put it somewhere designed for that - // rather than polluting the empty package with synthetics. - val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) - build.setInfo(ownerClass, ClassInfoType(List(ObjectTpe), newScope, ownerClass)) - val owner = ownerClass.newLocalDummy(expr.pos) - val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr, owner)) - val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) - val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) - def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - - val run = new Run - run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works - phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled - currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions - reporter.reset() - - val expr1 = wrapper(transform(currentTyper, expr)) - var (dummies1, unwrapped) = expr1 match { - case Block(dummies, unwrapped) => ((dummies, unwrapped)) - case unwrapped => ((Nil, unwrapped)) - } - val invertedIndex = freeTerms map (_.swap) - // todo. also fixup singleton types - unwrapped = new Transformer { - override def transform(tree: Tree): Tree = - tree match { - case Ident(name: TermName) if invertedIndex contains name => - Ident(invertedIndex(name)) setType tree.tpe - case _ => - super.transform(tree) - } - }.transform(unwrapped) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(unwrapped) - unwrapped = if (expr0.isTerm) unwrapped else unwrapFromTerm(unwrapped) - unwrapped + def transformDuringTyper(expr: Tree, mode: scala.reflect.internal.Mode, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { + def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) wrappingIntoTerm(tree)(op) else op(tree) + withWrapping(verify(expr))(expr1 => { + // need to extract free terms, because otherwise you won't be able to typecheck macros against something that contains them + val exprAndFreeTerms = extractFreeTerms(expr1, wrapFreeTermRefs = false) + var expr2 = exprAndFreeTerms._1 + val freeTerms = exprAndFreeTerms._2 + val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList + expr2 = Block(dummies, expr2) + + // !!! Why is this is in the empty package? If it's only to make + // it inaccessible then please put it somewhere designed for that + // rather than polluting the empty package with synthetics. + // [Eugene] how can we implement that? + val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) + build.setInfo(ownerClass, ClassInfoType(List(ObjectTpe), newScope, ownerClass)) + val owner = ownerClass.newLocalDummy(expr2.pos) + val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr2, owner)) + val withImplicitFlag = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) + val withMacroFlag = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) + def withContext (tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) + + val run = new Run + run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works + phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + reporter.reset() + + val expr3 = withContext(transform(currentTyper, expr2)) + var (dummies1, result) = expr3 match { + case Block(dummies, result) => ((dummies, result)) + case result => ((Nil, result)) + } + val invertedIndex = freeTerms map (_.swap) + result = new Transformer { + override def transform(tree: Tree): Tree = + tree match { + case Ident(name: TermName) if invertedIndex contains name => + Ident(invertedIndex(name)) setType tree.tpe + case _ => + super.transform(tree) + } + }.transform(result) + new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(result) + result + }) } def typecheck(expr: Tree, pt: Type, mode: scala.reflect.internal.Mode, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = - transformDuringTyper(expr, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( + transformDuringTyper(expr, mode, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( (currentTyper, expr) => { - trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymkinds.value)) + trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) currentTyper.silent(_.typed(expr, mode, pt), reportAmbiguousErrors = false) match { case analyzer.SilentResultValue(result) => trace("success: ")(showAttributed(result, true, true, settings.Yshowsymkinds.value)) @@ -187,9 +180,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => }) def inferImplicit(tree: Tree, pt: Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: Position): Tree = - transformDuringTyper(tree, withImplicitViewsDisabled = false, withMacrosDisabled = withMacrosDisabled)( + transformDuringTyper(tree, TERMmode, withImplicitViewsDisabled = false, withMacrosDisabled = withMacrosDisabled)( (currentTyper, tree) => { - trace("inferring implicit %s (macros = %s): ".format(if (isView) "view" else "value", !withMacrosDisabled))(showAttributed(pt, true, true, settings.Yshowsymkinds.value)) + trace("inferring implicit %s (macros = %s): ".format(if (isView) "view" else "value", !withMacrosDisabled))(showAttributed(pt, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) analyzer.inferImplicit(tree, pt, isView, currentTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw ToolBoxError(msg)) }) @@ -207,7 +200,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } def compile(expr0: Tree): () => Any = { - val expr = wrapIntoTerm(expr0) + val expr = build.SyntacticBlock(expr0 :: Nil) val freeTerms = expr.freeTerms // need to calculate them here, because later on they will be erased val thunks = freeTerms map (fte => () => fte.value) // need to be lazy in order not to distort evaluation order @@ -247,10 +240,10 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => List(), List(methdef), NoPosition)) - trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) + trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) val cleanedUp = resetAttrs(moduledef) - trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) + trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) cleanedUp.asInstanceOf[ModuleDef] } @@ -298,19 +291,22 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => tree } - def showAttributed(artifact: Any, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = { + def showAttributed(artifact: Any, printTypes: Boolean = true, printIds: Boolean = true, printOwners: Boolean = false, printKinds: Boolean = false): String = { val saved1 = settings.printtypes.value val saved2 = settings.uniqid.value - val saved3 = settings.Yshowsymkinds.value + val saved3 = settings.Yshowsymowners.value + val saved4 = settings.Yshowsymkinds.value try { settings.printtypes.value = printTypes settings.uniqid.value = printIds + settings.Yshowsymowners.value = printOwners settings.Yshowsymkinds.value = printKinds artifact.toString } finally { settings.printtypes.value = saved1 settings.uniqid.value = saved2 - settings.Yshowsymkinds.value = saved3 + settings.Yshowsymowners.value = saved3 + settings.Yshowsymkinds.value = saved4 } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 9f6807fe17..301e7051df 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -14,6 +14,7 @@ import scala.util.Try */ trait Parsers { self: Quasiquotes => import global.{Try => _, _} + import build.implodePatDefs abstract class Parser extends { val global: self.global.type = self.global @@ -61,12 +62,10 @@ trait Parsers { self: Quasiquotes => override implicit def fresh: FreshNameCreator = parser.fresh // q"(..$xs)" - override def makeTupleTerm(trees: List[Tree]): Tree = - Apply(Ident(nme.QUASIQUOTE_TUPLE), trees) + override def makeTupleTerm(trees: List[Tree]): Tree = TuplePlaceholder(trees) // tq"(..$xs)" - override def makeTupleType(trees: List[Tree]): Tree = - AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), trees) + override def makeTupleType(trees: List[Tree]): Tree = TupleTypePlaceholder(trees) // q"{ $x }" override def makeBlock(stats: List[Tree]): Tree = stats match { @@ -75,30 +74,32 @@ trait Parsers { self: Quasiquotes => } // tq"$a => $b" - override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = - AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), argtpes :+ restpe) + override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = FunctionTypePlaceholder(argtpes, restpe) + + // make q"val (x: T) = rhs" be equivalent to q"val x: T = rhs" for sake of bug compatibility (SI-8211) + override def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = pat match { + case TuplePlaceholder(inParensPat :: Nil) => super.makePatDef(mods, inParensPat, rhs) + case _ => super.makePatDef(mods, pat, rhs) + } } import treeBuilder.{global => _, unit => _, _} - def quasiquoteParam(name: Name, flags: FlagSet = NoFlags) = - ValDef(Modifiers(flags), name.toTermName, Ident(tpnme.QUASIQUOTE_PARAM), EmptyTree) - // q"def foo($x)" override def param(owner: Name, implicitmod: Int, caseParam: Boolean): ValDef = if (isHole && lookingAhead { in.token == COMMA || in.token == RPAREN }) { - quasiquoteParam(ident(), implicitmod) + ParamPlaceholder(implicitmod, ident()) } else super.param(owner, implicitmod, caseParam) // q"($x) => ..." && q"class X { selfie => } override def convertToParam(tree: Tree): ValDef = tree match { - case Ident(name) if isHole(name) => quasiquoteParam(name) + case Ident(name) if isHole(name) => ParamPlaceholder(NoFlags, name) case _ => super.convertToParam(tree) } // q"foo match { case $x }" override def caseClause(): CaseDef = if (isHole && lookingAhead { in.token == CASE || in.token == RBRACE || in.token == SEMI }) { - val c = makeCaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Ident(ident()))), EmptyTree, EmptyTree) + val c = CasePlaceholder(ident()) while (in.token == SEMI) in.nextToken() c } else @@ -132,7 +133,7 @@ trait Parsers { self: Quasiquotes => in.nextToken() annot :: readAnnots(annot) case _ if isHole && lookingAhead { isAnnotation || isModifier || isDefIntro || isIdent || isStatSep || in.token == LPAREN } => - val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString)))) + val ann = ModsPlaceholder(in.name) in.nextToken() ann :: readAnnots(annot) case _ => @@ -141,13 +142,13 @@ trait Parsers { self: Quasiquotes => override def refineStat(): List[Tree] = if (isHole && !isDclIntro) { - val result = ValDef(NoMods, in.name, Ident(tpnme.QUASIQUOTE_REFINE_STAT), EmptyTree) :: Nil + val result = RefineStatPlaceholder(in.name) :: Nil in.nextToken() result } else super.refineStat() override def ensureEarlyDef(tree: Tree) = tree match { - case Ident(name: TermName) if isHole(name) => ValDef(NoMods | Flag.PRESUPER, name, Ident(tpnme.QUASIQUOTE_EARLY_DEF), EmptyTree) + case Ident(name: TermName) if isHole(name) => EarlyDefPlaceholder(name) case _ => super.ensureEarlyDef(tree) } @@ -158,14 +159,14 @@ trait Parsers { self: Quasiquotes => override def topStat = super.topStat.orElse { case _ if isHole => - val stats = ValDef(NoMods, in.name, Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) :: Nil + val stats = PackageStatPlaceholder(in.name) :: Nil in.nextToken() stats } override def enumerator(isFirst: Boolean, allowNestedIf: Boolean = true) = if (isHole && lookingAhead { in.token == EOF || in.token == RPAREN || isStatSep }) { - val res = build.SyntacticValFrom(Bind(in.name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) :: Nil + val res = ForEnumPlaceholder(in.name) :: Nil in.nextToken() res } else super.enumerator(isFirst, allowNestedIf) @@ -182,7 +183,7 @@ trait Parsers { self: Quasiquotes => } object TermParser extends Parser { - def entryPoint = parser => Q(gen.mkTreeOrBlock(parser.templateOrTopStatSeq())) + def entryPoint = parser => Q(implodePatDefs(gen.mkTreeOrBlock(parser.templateOrTopStatSeq()))) } object TypeParser extends Parser { @@ -195,7 +196,7 @@ trait Parsers { self: Quasiquotes => } object CaseParser extends Parser { - def entryPoint = _.caseClause() + def entryPoint = parser => implodePatDefs(parser.caseClause()) } object PatternParser extends Parser { @@ -209,7 +210,7 @@ trait Parsers { self: Quasiquotes => def entryPoint = { parser => val enums = parser.enumerator(isFirst = false, allowNestedIf = false) assert(enums.length == 1) - enums.head + implodePatDefs(enums.head) } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index 29df0ae670..e7730f878f 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -100,6 +100,8 @@ trait Placeholders { self: Quasiquotes => } object ModsPlaceholder extends HolePlaceholder { + def apply(name: Name) = + Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(name.toString)))) def matching = { case Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(s: String)))) => TermName(s) } @@ -112,12 +114,16 @@ trait Placeholders { self: Quasiquotes => } object ParamPlaceholder extends HolePlaceholder { + def apply(flags: FlagSet, name: Name) = + ValDef(Modifiers(flags), nme.QUASIQUOTE_PARAM, Ident(name), EmptyTree) def matching = { - case ValDef(_, name, Ident(tpnme.QUASIQUOTE_PARAM), EmptyTree) => name + case ValDef(_, nme.QUASIQUOTE_PARAM, Ident(name), EmptyTree) => name } } object TuplePlaceholder { + def apply(args: List[Tree]) = + Apply(Ident(nme.QUASIQUOTE_TUPLE), args) def unapply(tree: Tree): Option[List[Tree]] = tree match { case Apply(Ident(nme.QUASIQUOTE_TUPLE), args) => Some(args) case _ => None @@ -125,6 +131,8 @@ trait Placeholders { self: Quasiquotes => } object TupleTypePlaceholder { + def apply(args: List[Tree]) = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) def unapply(tree: Tree): Option[List[Tree]] = tree match { case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) => Some(args) case _ => None @@ -132,6 +140,8 @@ trait Placeholders { self: Quasiquotes => } object FunctionTypePlaceholder { + def apply(args: List[Tree], res: Tree) = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) => Some((args, res)) case _ => None @@ -146,6 +156,8 @@ trait Placeholders { self: Quasiquotes => } object CasePlaceholder { + def apply(name: Name) = + CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), Ident(name) :: Nil), EmptyTree, EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(hole))), EmptyTree, EmptyTree) => Some(hole) case _ => None @@ -153,27 +165,35 @@ trait Placeholders { self: Quasiquotes => } object RefineStatPlaceholder { + def apply(name: Name) = + ValDef(NoMods, nme.QUASIQUOTE_REFINE_STAT, Ident(name), EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { - case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some(hole) + case ValDef(_, nme.QUASIQUOTE_REFINE_STAT, Ident(Placeholder(hole)), _) => Some(hole) case _ => None } } object EarlyDefPlaceholder { + def apply(name: Name) = + ValDef(Modifiers(Flag.PRESUPER), nme.QUASIQUOTE_EARLY_DEF, Ident(name), EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { - case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some(hole) + case ValDef(_, nme.QUASIQUOTE_EARLY_DEF, Ident(Placeholder(hole)), _) => Some(hole) case _ => None } } object PackageStatPlaceholder { + def apply(name: Name) = + ValDef(NoMods, nme.QUASIQUOTE_PACKAGE_STAT, Ident(name), EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { - case ValDef(NoMods, Placeholder(hole), Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) => Some(hole) + case ValDef(NoMods, nme.QUASIQUOTE_PACKAGE_STAT, Ident(Placeholder(hole)), EmptyTree) => Some(hole) case _ => None } } object ForEnumPlaceholder { + def apply(name: Name) = + build.SyntacticValFrom(Bind(name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) def unapply(tree: Tree): Option[Hole] = tree match { case build.SyntacticValFrom(Bind(Placeholder(hole), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) => Some(hole) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index af8b0be92c..339937adc3 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -194,8 +194,8 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticEmptyTypeTree) case SyntacticImport(expr, selectors) => reifyBuildCall(nme.SyntacticImport, expr, selectors) - case Q(Placeholder(Hole(tree, DotDot))) => - mirrorBuildCall(nme.SyntacticBlock, tree) + case Q(tree) if fillListHole.isDefinedAt(tree) => + mirrorBuildCall(nme.SyntacticBlock, fillListHole(tree)) case Q(other) => reifyTree(other) // Syntactic block always matches so we have to be careful @@ -311,11 +311,7 @@ trait Reifiers { self: Quasiquotes => */ def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree - /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put - * in the correct position. Fallbacks to regular reification for non-high cardinality - * elements. - */ - override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) { + val fillListHole: PartialFunction[Any, Tree] = { case Placeholder(Hole(tree, DotDot)) => tree case CasePlaceholder(Hole(tree, DotDot)) => tree case RefineStatPlaceholder(h @ Hole(_, DotDot)) => reifyRefineStat(h) @@ -323,12 +319,23 @@ trait Reifiers { self: Quasiquotes => case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h) case ForEnumPlaceholder(Hole(tree, DotDot)) => tree case ParamPlaceholder(Hole(tree, DotDot)) => tree + case SyntacticPatDef(mods, pat, tpt, rhs) => + reifyBuildCall(nme.SyntacticPatDef, mods, pat, tpt, rhs) + case SyntacticValDef(mods, p @ Placeholder(h: ApplyHole), tpt, rhs) if h.tpe <:< treeType => + mirrorBuildCall(nme.SyntacticPatDef, reify(mods), h.tree, reify(tpt), reify(rhs)) + } + + val fillListOfListsHole: PartialFunction[Any, Tree] = { case List(ParamPlaceholder(Hole(tree, DotDotDot))) => tree case List(Placeholder(Hole(tree, DotDotDot))) => tree - } { - reify(_) } + /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put + * in the correct position. Fallbacks to regular reification for non-high cardinality + * elements. + */ + override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs)(fillListHole.orElse(fillListOfListsHole))(reify) + def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h) } { |