diff options
39 files changed, 538 insertions, 361 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala index 8b92f0acd6..885fc3f518 100644 --- a/src/compiler/scala/tools/nsc/ast/Printers.scala +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -278,6 +278,7 @@ trait Printers extends reflect.internal.Printers { this: Global => def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) + def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true) def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newStandardTreePrinter(stream: OutputStream): TreePrinter = newStandardTreePrinter(new PrintWriter(stream)) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 9ed9cfe267..73f5ec7b3c 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -191,7 +191,7 @@ abstract class TreeBuilder { } else { val x = freshTermName() Block( - List(ValDef(Modifiers(SYNTHETIC | HIDDEN), x, TypeTree(), stripParens(left))), + List(ValDef(Modifiers(SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))), Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) } } else { @@ -488,7 +488,7 @@ abstract class TreeBuilder { def makeCatchFromExpr(catchExpr: Tree): CaseDef = { val binder = freshTermName("x") val pat = Bind(binder, Typed(Ident(nme.WILDCARD), Ident(tpnme.Throwable))) - val catchDef = ValDef(Modifiers(HIDDEN), freshTermName("catchExpr"), TypeTree(), catchExpr) + val catchDef = ValDef(Modifiers(ARTIFACT), freshTermName("catchExpr"), TypeTree(), catchExpr) val catchFn = Ident(catchDef.name) val body = atPos(catchExpr.pos.makeTransparent)(Block( List(catchDef), @@ -562,7 +562,7 @@ abstract class TreeBuilder { val tmp = freshTermName() val firstDef = atPos(matchExpr.pos) { - ValDef(Modifiers(PrivateLocal | SYNTHETIC | HIDDEN | (mods.flags & LAZY)), + ValDef(Modifiers(PrivateLocal | SYNTHETIC | ARTIFACT | (mods.flags & LAZY)), tmp, TypeTree(), matchExpr) } var cnt = 0 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index bbd826ee26..1a9201fd67 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -295,7 +295,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0, if (sym.isStaticMember) ACC_STATIC else 0, if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, - if (sym.isHidden) ACC_SYNTHETIC else 0, + if (sym.isArtifact) ACC_SYNTHETIC else 0, if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, if (sym.isVarargsMethod) ACC_VARARGS else 0, if (sym.hasFlag(Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0 @@ -851,7 +851,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // generic information could disappear as a consequence of a seemingly // unrelated change. settings.Ynogenericsig.value - || sym.isHidden + || sym.isArtifact || sym.isLiftedMethod || sym.isBridge || (sym.ownerChain exists (_.isImplClass)) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index e7e47a9ea8..d3b24a19f4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -728,7 +728,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // generic information could disappear as a consequence of a seemingly // unrelated change. settings.Ynogenericsig.value - || sym.isHidden + || sym.isArtifact || sym.isLiftedMethod || sym.isBridge || (sym.ownerChain exists (_.isImplClass)) @@ -866,7 +866,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with def genField(f: IField) { debuglog("Adding field: " + f.symbol.fullName) - + val jfield = jclass.addNewField( javaFieldFlags(f.symbol), javaName(f.symbol), @@ -1021,7 +1021,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with method = m jmethod = clinitMethod - + computeLocalVarsIndex(m) genCode(m) case None => @@ -1116,7 +1116,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name } toSet } debuglog("Potentially conflicting names for forwarders: " + conflictingNames) - + for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flags.METHOD)) { if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor) debuglog("No forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) @@ -1308,7 +1308,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with jclass.getType()) } } - + style match { case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) @@ -1815,7 +1815,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with * Synthetic locals are skipped. All variables are method-scoped. */ private def genLocalVariableTable(m: IMethod, jcode: JCode) { - val vars = m.locals filterNot (_.sym.isHidden) + val vars = m.locals filterNot (_.sym.isArtifact) if (vars.isEmpty) return val pool = jclass.getConstantPool @@ -1889,7 +1889,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with */ def computeLocalVarsIndex(m: IMethod) { var idx = if (m.symbol.isStaticMember) 0 else 1; - + for (l <- m.params) { debuglog("Index value for " + l + "{" + l.## + "}: " + idx) l.index = idx @@ -1977,7 +1977,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0, if (sym.isStaticMember) ACC_STATIC else 0, if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, - if (sym.isHidden) ACC_SYNTHETIC else 0, + if (sym.isArtifact) ACC_SYNTHETIC else 0, if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, if (sym.isVarargsMethod) ACC_VARARGS else 0, if (sym.hasFlag(Flags.SYNCHRONIZED)) JAVA_ACC_SYNCHRONIZED else 0 diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 720b1347ef..dbc34bd7b3 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -111,6 +111,12 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "only use it if you haven't defined usecase for implicitly inherited members." ) + val docImplicitsHide = MultiStringSetting ( + "-implicits-hide", + "implicit(s)", + "Hide the members inherited by the given comma separated, fully qualified implicit conversions. Add dot (.) to include default conversions." + ) + val docDiagrams = BooleanSetting ( "-diagrams", "Create inheritance diagrams for classes, traits and packages." @@ -203,7 +209,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator, docRootContent, useStupidTypes, docDiagrams, docDiagramsDebug, docDiagramsDotPath, docDiagramsDotTimeout, docDiagramsDotRestart, - docImplicits, docImplicitsDebug, docImplicitsShowAll, + docImplicits, docImplicitsDebug, docImplicitsShowAll, docImplicitsHide, docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses, docNoPrefixes, docNoLinkWarnings, docRawOutput, docSkipPackages, docExpandAllTypes, docGroups @@ -224,6 +230,14 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) def skipPackage(qname: String) = skipPackageNames(qname.toLowerCase) + lazy val hiddenImplicits: Set[String] = { + if (docImplicitsHide.value.isEmpty) hardcoded.commonConversionTargets + else docImplicitsHide.value.toSet flatMap { name: String => + if(name == ".") hardcoded.commonConversionTargets + else Set(name) + } + } + /** * This is the hardcoded area of Scaladoc. This is where "undesirable" stuff gets eliminated. I know it's not pretty, * but ultimately scaladoc has to be useful. :) @@ -264,7 +278,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) } /** Common conversion targets that affect any class in Scala */ - val commonConversionTargets = List( + val commonConversionTargets = Set( "scala.Predef.any2stringfmt", "scala.Predef.any2stringadd", "scala.Predef.any2ArrowAssoc", diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index 5977acc0c2..1f68781777 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -148,8 +148,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp <div id="ancestors"> <span class="filtertype">Implicitly<br/> </span> - <ol id="implicits"> - { tpl.conversions.map(conv => <li class="in" name={ conv.conversionQualifiedName }><span>{ "by " + conv.conversionShortName }</span></li>) } + <ol id="implicits"> { + tpl.conversions.map { conv => + val name = conv.conversionQualifiedName + val hide = universe.settings.hiddenImplicits(name) + <li class="in" name={ name } data-hidden={ hide.toString }><span>{ "by " + conv.conversionShortName }</span></li> + } + } </ol> </div> else NodeSeq.Empty diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index afd0293fe1..5920fdfdb2 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -4,12 +4,11 @@ $(document).ready(function(){ var isHiddenClass = function (name) { return name == 'scala.Any' || - name == 'scala.AnyRef' || - name == 'scala.Predef.any2stringfmt' || - name == 'scala.Predef.any2stringadd' || - name == 'scala.Predef.any2ArrowAssoc' || - name == 'scala.Predef.any2Ensuring' || - name == 'scala.collection.TraversableOnce.alternateImplicit' + name == 'scala.AnyRef'; + }; + + var isHidden = function (elem) { + return $(elem).attr("data-hidden") == 'true'; }; $("#linearization li:gt(0)").filter(function(){ @@ -17,7 +16,7 @@ $(document).ready(function(){ }).removeClass("in").addClass("out"); $("#implicits li").filter(function(){ - return isHiddenClass($(this).attr("name")); + return isHidden(this); }).removeClass("in").addClass("out"); // Pre-filter members @@ -113,7 +112,7 @@ $(document).ready(function(){ var filteredImplicits = $("#implicits li.out").filter(function() { - return ! isHiddenClass($(this).attr("name")); + return ! isHidden(this); }); filteredImplicits.removeClass("out").addClass("in"); diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index 46b2a11d4a..6d193c30f7 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -530,8 +530,8 @@ trait ImplicitConversion { /** The members inherited by this implicit conversion */ def members: List[MemberEntity] - /** Is this a common implicit conversion (aka conversion that affects all classes, in Predef?) */ - def isCommonConversion: Boolean + /** Is this a hidden implicit conversion (as specified in the settings) */ + def isHiddenConversion: Boolean } /** Shadowing captures the information that the member is shadowed by some other members diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index a962ec4007..ed8541f692 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -1075,7 +1075,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def classExcluded(clazz: TemplateEntity): Boolean = settings.hardcoded.isExcluded(clazz.qualifiedName) // the implicit conversions that are excluded from the pages should not appear in the diagram - def implicitExcluded(convertorMethod: String): Boolean = settings.hardcoded.commonConversionTargets.contains(convertorMethod) + def implicitExcluded(convertorMethod: String): Boolean = settings.hiddenImplicits(convertorMethod) // whether or not to create a page for an {abstract,alias} type def typeShouldDocument(bSym: Symbol, inTpl: DocTemplateImpl) = diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 5a0cc602e5..327436ed20 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -113,9 +113,9 @@ trait ModelFactoryImplicitSupport { conversions = conversions.filter((ic: ImplicitConversionImpl) => hardcoded.valueClassFilter(sym.nameString, ic.conversionQualifiedName)) - // Put the class-specific conversions in front + // Put the visible conversions in front val (ownConversions, commonConversions) = - conversions.partition(!_.isCommonConversion) + conversions.partition(!_.isHiddenConversion) ownConversions ::: commonConversions } @@ -416,7 +416,7 @@ trait ModelFactoryImplicitSupport { lazy val members: List[MemberEntity] = memberImpls - def isCommonConversion = hardcoded.commonConversionTargets.contains(conversionQualifiedName) + def isHiddenConversion = settings.hiddenImplicits(conversionQualifiedName) override def toString = "Implcit conversion from " + sym.tpe + " to " + toType + " done by " + convSym } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 66a815465d..b358785714 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -869,10 +869,10 @@ abstract class ClassfileParser { } else in.skip(attrLen) case tpnme.SyntheticATTR => - sym.setFlag(SYNTHETIC | HIDDEN) + sym.setFlag(SYNTHETIC | ARTIFACT) in.skip(attrLen) case tpnme.BridgeATTR => - sym.setFlag(BRIDGE | HIDDEN) + sym.setFlag(BRIDGE | ARTIFACT) in.skip(attrLen) case tpnme.DeprecatedATTR => val arg = Literal(Constant("see corresponding Javadoc for more information.")) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 4c4b7dcff4..4715a5d340 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -413,7 +413,7 @@ abstract class Erasure extends AddInterfaces if (!bridgeNeeded) return - val newFlags = (member.flags | BRIDGE | HIDDEN) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED) + val newFlags = (member.flags | BRIDGE | ARTIFACT) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED) val bridge = other.cloneSymbolImpl(owner, newFlags) setPos owner.pos debuglog("generating bridge from %s (%s): %s to %s: %s".format( diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 34f5ac611c..c5494b5b1f 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -95,7 +95,7 @@ abstract class ExplicitOuter extends InfoTransform else findOrElse(clazz.info.decls)(_.outerSource == clazz)(NoSymbol) } def newOuterAccessor(clazz: Symbol) = { - val accFlags = SYNTHETIC | HIDDEN | METHOD | STABLE | ( if (clazz.isTrait) DEFERRED else 0 ) + val accFlags = SYNTHETIC | ARTIFACT | METHOD | STABLE | ( if (clazz.isTrait) DEFERRED else 0 ) val sym = clazz.newMethod(nme.OUTER, clazz.pos, accFlags) val restpe = if (clazz.isTrait) clazz.outerClass.tpe else clazz.outerClass.thisType @@ -104,7 +104,7 @@ abstract class ExplicitOuter extends InfoTransform sym setInfo MethodType(Nil, restpe) } def newOuterField(clazz: Symbol) = { - val accFlags = SYNTHETIC | HIDDEN | PARAMACCESSOR | ( if (clazz.isEffectivelyFinal) PrivateLocal else PROTECTED ) + val accFlags = SYNTHETIC | ARTIFACT | PARAMACCESSOR | ( if (clazz.isEffectivelyFinal) PrivateLocal else PROTECTED ) val sym = clazz.newValue(nme.OUTER_LOCAL, clazz.pos, accFlags) sym setInfo clazz.outerClass.thisType diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 0579d7bb96..982b7a680e 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -35,7 +35,7 @@ abstract class OverridingPairs { * */ protected def exclude(sym: Symbol): Boolean = - sym.isConstructor || sym.isPrivateLocal || sym.isHidden + sym.isConstructor || sym.isPrivateLocal || sym.isArtifact /** The parents of base (may also be refined). */ diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index e6c886b6d6..58914b61d8 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -797,7 +797,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { var specializingOn = specializedParams(sym) val unusedStvars = specializingOn filterNot specializedTypeVars(sym.info) - // I think the last condition should be !sym.isHidden, but that made the + // I think the last condition should be !sym.isArtifact, but that made the // compiler start warning about Tuple1.scala and Tuple2.scala claiming // their type parameters are used in non-specializable positions. Why is // unusedStvars.nonEmpty for these classes??? diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 7c5d458fee..c8bf70e9e0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -11,6 +11,7 @@ import reflect.internal.util.Statistics import scala.reflect.macros.util._ import java.lang.{Class => jClass} import java.lang.reflect.{Array => jArray, Method => jMethod} +import scala.reflect.internal.util.Collections._ /** * Code to deal with macros, namely with: @@ -48,6 +49,171 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val globalMacroCache = collection.mutable.Map[Any, Any]() val perRunMacroCache = perRunCaches.newMap[Symbol, collection.mutable.Map[Any, Any]] + /** `MacroImplBinding` and its companion module are responsible for + * serialization/deserialization of macro def -> impl bindings. + * + * The first officially released version of macros persisted these bindings across compilation runs + * using a neat trick. The right-hand side of a macro definition (which contains a reference to a macro impl) + * was typechecked and then put verbatim into an annotation on the macro definition. + * + * This solution is very simple, but unfortunately it's also lacking. If we use it, then + * signatures of macro defs become transitively dependent on scala-reflect.jar + * (because they refer to macro impls, and macro impls refer to scala.reflect.macros.Context defined in scala-reflect.jar). + * More details can be found in comments to https://issues.scala-lang.org/browse/SI-5940. + * + * Therefore we have to avoid putting macro impls into binding pickles and come up with our own serialization format. + * Situation is further complicated by the fact that it's not enough to just pickle macro impl's class name and method name, + * because macro expansion needs some knowledge about the shape of macro impl's signature (which we can't pickle). + * Hence we precompute necessary stuff (e.g. the layout of type parameters) when compiling macro defs. + */ + + /** Represents all the information that a macro definition needs to know about its implementation. + * Includes a path to load the implementation via Java reflection, + * and various accounting information necessary when composing an argument list for the reflective invocation. + */ + private case class MacroImplBinding( + // Java class name of the class that contains the macro implementation + // is used to load the corresponding object with Java reflection + val className: String, + // method name of the macro implementation + // `className` and `methName` are all we need to reflectively invoke a macro implementation + // because macro implementations cannot be overloaded + val methName: String, + // flattens the macro impl's parameter lists having symbols replaced with metadata + // currently metadata is an index of the type parameter corresponding to that type tag (if applicable) + // f.ex. for: def impl[T: AbsTypeTag, U: AbsTypeTag, V](c: Context)(x: c.Expr[T]): (U, V) = ??? + // `signature` will be equal to List(-1, -1, 0, 1) + val signature: List[Int], + // type arguments part of a macro impl ref (the right-hand side of a macro definition) + // these trees don't refer to a macro impl, so we can pickle them as is + val targs: List[Tree]) + + /** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation + * with synthetic content that carries the payload described in `MacroImplBinding`. + * + * For example, for a pair of macro definition and macro implementation: + * def impl(c: scala.reflect.macros.Context): c.Expr[Unit] = c.literalUnit; + * def foo: Unit = macro impl + * + * We will have the following annotation added on the macro definition `foo`: + * + * @scala.reflect.macros.internal.macroImpl( + * `macro`( + * "signature" = List(-1), + * "methodName" = "impl", + * "versionFormat" = 1, + * "className" = "Macros$")) + */ + private object MacroImplBinding { + val versionFormat = 1 + + def pickleAtom(obj: Any): Tree = + obj match { + case list: List[_] => Apply(Ident(ListModule), list map pickleAtom) + case s: String => Literal(Constant(s)) + case i: Int => Literal(Constant(i)) + } + + def unpickleAtom(tree: Tree): Any = + tree match { + case Apply(list @ Ident(_), args) if list.symbol == ListModule => args map unpickleAtom + case Literal(Constant(s: String)) => s + case Literal(Constant(i: Int)) => i + } + + def pickle(macroImplRef: Tree): Tree = { + val macroImpl = macroImplRef.symbol + val paramss = macroImpl.paramss + + // this logic relies on the assumptions that were valid for the old macro prototype + // namely that macro implementations can only be defined in top-level classes and modules + // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different + // for example, a macro def could be defined in a trait that is implemented by an object + // there are some more clever cases when seemingly non-static method ends up being statically accessible + // however, the code below doesn't account for these guys, because it'd take a look of time to get it right + // for now I leave it as a todo and move along to more the important stuff + // [Eugene] relies on the fact that macro implementations can only be defined in static classes + // [Martin to Eugene++] There's similar logic buried in Symbol#flatname. Maybe we can refactor? + // [Eugene] we will refactor once I get my hands on https://issues.scala-lang.org/browse/SI-5498 + def className: String = { + def loop(sym: Symbol): String = sym match { + case sym if sym.owner.isPackageClass => + val suffix = if (sym.isModuleClass) "$" else "" + sym.fullName + suffix + case sym => + val separator = if (sym.owner.isModuleClass) "" else "$" + loop(sym.owner) + separator + sym.javaSimpleName.toString + } + + loop(macroImpl.owner.enclClass) + } + + def signature: List[Int] = { + val transformed = transformTypeTagEvidenceParams(paramss, (param, tparam) => tparam) + transformed.flatten map (p => if (p.isTerm) -1 else p.paramPos) + } + + val payload = List[(String, Any)]( + "versionFormat" -> versionFormat, + "className" -> className, + "methodName" -> macroImpl.name.toString, + "signature" -> signature + ) + + // the shape of the nucleus is chosen arbitrarily. it doesn't carry any payload. + // it's only necessary as a stub `fun` for an Apply node that carries metadata in its `args` + // so don't try to find a program element named "macro" that corresponds to the nucleus + // I just named it "macro", because it's macro-related, but I could as well name it "foobar" + val nucleus = Ident(newTermName("macro")) + val wrapped = Apply(nucleus, payload map { case (k, v) => Assign(pickleAtom(k), pickleAtom(v)) }) + val pickle = gen.mkTypeApply(wrapped, treeInfo.typeArguments(macroImplRef.duplicate)) + + // assign NoType to all freshly created AST nodes + // otherwise pickler will choke on tree.tpe being null + // there's another gotcha + // if you don't assign a ConstantType to a constant + // then pickling will crash + new Transformer { + override def transform(tree: Tree) = { + tree match { + case Literal(const @ Constant(x)) if tree.tpe == null => tree setType ConstantType(const) + case _ if tree.tpe == null => tree setType NoType + case _ => ; + } + super.transform(tree) + } + }.transform(pickle) + } + + def unpickle(pickle: Tree): MacroImplBinding = { + val (wrapped, targs) = + pickle match { + case TypeApply(wrapped, targs) => (wrapped, targs) + case wrapped => (wrapped, Nil) + } + val Apply(_, pickledPayload) = wrapped + val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap + + val pickleVersionFormat = payload("versionFormat").asInstanceOf[Int] + if (versionFormat != pickleVersionFormat) throw new Error("macro impl binding format mismatch: expected $versionFormat, actual $pickleVersionFormat") + + val className = payload("className").asInstanceOf[String] + val methodName = payload("methodName").asInstanceOf[String] + val signature = payload("signature").asInstanceOf[List[Int]] + MacroImplBinding(className, methodName, signature, targs) + } + } + + private def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { + val pickle = MacroImplBinding.pickle(macroImplRef) + macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(pickle), Nil) + } + + private def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = { + val Some(AnnotationInfo(_, List(pickle), _)) = macroDef.getAnnotation(MacroImplAnnotation) + MacroImplBinding.unpickle(pickle) + } + /** A list of compatible macro implementation signatures. * * In the example above: @@ -144,32 +310,22 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroTraceVerbose("macroImplSigs are: ")(paramsss, implRetTpe) } - private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Option[Symbol]): List[List[Symbol]] = { - if (paramss.length == 0) + private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { + import definitions.{ AbsTypeTagClass, MacroContextClass } + if (paramss.isEmpty || paramss.last.isEmpty) return paramss - val wannabe = if (paramss.head.length == 1) paramss.head.head else NoSymbol - val contextParam = if (wannabe != NoSymbol && wannabe.tpe <:< definitions.MacroContextClass.tpe) wannabe else NoSymbol - - val lastParamList0 = paramss.lastOption getOrElse Nil - val lastParamList = lastParamList0 flatMap (param => param.tpe match { - case TypeRef(SingleType(NoPrefix, contextParam), sym, List(tparam)) => - var wannabe = sym - while (wannabe.isAliasType) wannabe = wannabe.info.typeSymbol - if (wannabe != definitions.AbsTypeTagClass) - List(param) - else - transform(param, tparam.typeSymbol) map (_ :: Nil) getOrElse Nil - case _ => - List(param) - }) - - var result = paramss.dropRight(1) :+ lastParamList - if (lastParamList0.isEmpty ^ lastParamList.isEmpty) { - result = result dropRight 1 + val ContextParam = paramss.head match { + case p :: Nil => p filter (_.tpe <:< definitions.MacroContextClass.tpe) + case _ => NoSymbol } - - result + def isTag(sym: Symbol): Boolean = (sym == AbsTypeTagClass) || (sym.isAliasType && isTag(sym.info.typeSymbol)) + def transformTag(param: Symbol): Symbol = param.tpe match { + case TypeRef(SingleType(NoPrefix, ContextParam), sym, tp :: Nil) if isTag(sym) => transform(param, tp.typeSymbol) + case _ => param + } + val last = paramss.last map transformTag filterNot (_ eq NoSymbol) + if (last.isEmpty) paramss.init else paramss.init :+ last } /** As specified above, body of a macro definition must reference its implementation. @@ -186,6 +342,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { import typer.context macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) + val macroDef = ddef.symbol + val defpos = macroDef.pos + val implpos = ddef.rhs.pos + assert(macroDef.isTermMacro, ddef) + if (fastTrack contains ddef.symbol) { macroLogVerbose("typecheck terminated unexpectedly: macro is hardwired") assert(!ddef.tpt.isEmpty, "hardwired macros must provide result type") @@ -207,17 +368,18 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } } - var hasErrors = false + var _hasError = false + def hasError = _hasError + def setError(): Unit = { + _hasError = true + macroDef setFlag IS_ERROR + } def reportError(pos: Position, msg: String) = { - hasErrors = true + setError() context.error(pos, msg) + macroDef setFlag IS_ERROR } - val macroDef = ddef.symbol - val defpos = macroDef.pos - val implpos = ddef.rhs.pos - assert(macroDef.isTermMacro, ddef) - def invalidBodyError() = reportError(defpos, "macro body has wrong shape:" + @@ -275,7 +437,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val rhs = ddef.rhs validatePreTyper(rhs) - if (hasErrors) macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) + if (hasError) macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) // we use typed1 instead of typed, because otherwise adapt is going to mess us up // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail @@ -284,7 +446,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { def typecheckRhs(rhs: Tree): Tree = { try { val prevNumErrors = reporter.ERROR.count // [Eugene] funnily enough, the isErroneous check is not enough - var rhs1 = if (hasErrors) EmptyTree else typer.typed1(rhs, EXPRmode, WildcardType) + var rhs1 = if (hasError) EmptyTree else typer.typed1(rhs, EXPRmode, WildcardType) def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous while (!typecheckedWithErrors && rhsNeedsMacroExpansion) { @@ -313,35 +475,41 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val prevNumErrors = reporter.ERROR.count // funnily enough, the isErroneous check is not enough var rhs1 = typecheckRhs(rhs) - def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors - hasErrors = hasErrors || typecheckedWithErrors - if (typecheckedWithErrors) macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef) - val macroImpl = rhs1.symbol - macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(rhs1), Nil) - if (!hasErrors) { - if (macroImpl == null) { - invalidBodyError() - } else { - if (!macroImpl.isMethod) + def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors + if (typecheckedWithErrors) { + setError() + macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef) + } else { + if (!hasError) { + if (macroImpl == null) { invalidBodyError() - if (macroImpl.isOverloaded) - reportError(implpos, "macro implementation cannot be overloaded") - if (!macroImpl.typeParams.isEmpty && (!rhs1.isInstanceOf[TypeApply])) - reportError(implpos, "macro implementation reference needs type arguments") - if (!hasErrors) - validatePostTyper(rhs1) + } else { + if (!macroImpl.isMethod) + invalidBodyError() + if (!macroImpl.isPublic) + reportError(implpos, "macro implementation must be public") + if (macroImpl.isOverloaded) + reportError(implpos, "macro implementation cannot be overloaded") + if (!macroImpl.typeParams.isEmpty && (!rhs1.isInstanceOf[TypeApply])) + reportError(implpos, "macro implementation reference needs type arguments") + if (!hasError) + validatePostTyper(rhs1) + } + if (hasError) + macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) + } + if (!hasError) { + bindMacroImpl(macroDef, rhs1) // we must bind right over here, because return type inference needs this info } - if (hasErrors) - macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) } - if (!hasErrors) { + if (!hasError) { def checkCompatibility(reqparamss: List[List[Symbol]], actparamss: List[List[Symbol]], reqres: Type, actres: Type): List[String] = { - var hasErrors = false + var hasError = false var errors = List[String]() def compatibilityError(msg: String) { - hasErrors = true + hasError = true errors :+= msg } @@ -364,7 +532,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } } - if (!hasErrors) { + if (!hasError) { try { for ((rparams, aparams) <- reqparamss zip actparamss) { if (rparams.length < aparams.length) @@ -374,7 +542,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } // if the implementation signature is already deemed to be incompatible, we bail out // otherwise, high-order type magic employed below might crash in weird ways - if (!hasErrors) { + if (!hasError) { for ((rparams, aparams) <- reqparamss zip actparamss) { for ((rparam, aparam) <- rparams zip aparams) { def isRepeated(param: Symbol) = param.tpe.typeSymbol == RepeatedParamClass @@ -387,7 +555,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { compatibilityError("types incompatible for parameter "+rparam.name+": corresponding is not a vararg parameter") if (!isRepeated(rparam) && isRepeated(aparam)) compatibilityError("types incompatible for parameter "+aparam.name+": corresponding is not a vararg parameter") - if (!hasErrors) { + if (!hasError) { var atpe = aparam.tpe.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars) atpe = atpe.dealias // SI-5706 // strip the { type PrefixType = ... } refinement off the Context or otherwise we get compatibility errors @@ -400,11 +568,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } } } - if (!hasErrors) { + if (!hasError) { val atpe = actres.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars) checkSubType("return type", atpe, reqres) } - if (!hasErrors) { + if (!hasError) { val targs = solvedTypes(tvars, tparams, tparams map varianceInType(actres), false, lubDepth(flatactparams map (_.tpe)) max lubDepth(flatreqparams map (_.tpe))) val boundsOk = typer.silent(_.infer.checkBounds(ddef, NoPrefix, NoSymbol, tparams, targs, "")) @@ -429,7 +597,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } var actparamss = macroImpl.paramss - actparamss = transformTypeTagEvidenceParams(actparamss, (param, tparam) => None) + actparamss = transformTypeTagEvidenceParams(actparamss, (param, tparam) => NoSymbol) val rettpe = if (!ddef.tpt.isEmpty) typer.typedType(ddef.tpt).tpe else computeMacroDefTypeFromMacroImpl(ddef, macroDef, macroImpl) val (reqparamsss0, reqres0) = macroImplSigs(macroDef, ddef.tparams, ddef.vparamss, rettpe) @@ -443,7 +611,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) } - if (!hasErrors) { + if (!hasError) { val reqres = reqres0 val actres = macroImpl.tpe.finalResultType def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = { @@ -482,15 +650,6 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } } - // if this macro definition is erroneous, then there's no sense in expanding its usages - // in the previous prototype macro implementations were magically generated from macro definitions - // so macro definitions and its usages couldn't be compiled in the same compilation run - // however, now definitions and implementations are decoupled, so it's everything is possible - // hence, we now use IS_ERROR flag to serve as an indicator that given macro definition is broken - if (hasErrors) { - macroDef setFlag IS_ERROR - } - rhs1 } @@ -526,17 +685,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // sym.paramPos is unreliable (see another case below) val tparams = macroImpl.typeParams map (_.deSkolemize) val paramPos = tparams indexOf sym.deSkolemize - val sym1 = if (paramPos == -1) sym else { - val ann = macroDef.getAnnotation(MacroImplAnnotation) - ann match { - case Some(ann) => - val TypeApply(_, implRefTargs) = ann.args(0) - val implRefTarg = implRefTargs(paramPos).tpe.typeSymbol - implRefTarg - case None => - sym - } - } + val sym1 = + if (paramPos == -1) sym + else loadMacroImplBinding(macroDef).targs(paramPos).tpe.typeSymbol TypeRef(pre, sym1, args) case tpe => tpe @@ -550,7 +701,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // val defParamss = macroDef.paramss val defParamss = mmap(macroDdef.vparamss)(_.symbol) var implParamss = macroImpl.paramss - implParamss = transformTypeTagEvidenceParams(implParamss, (param, tparam) => None) + implParamss = transformTypeTagEvidenceParams(implParamss, (param, tparam) => NoSymbol) val implCtxParam = if (implParamss.length > 0 && implParamss(0).length > 0) implParamss(0)(0) else null def implParamToDefParam(implParam: Symbol): Symbol = { @@ -619,118 +770,42 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * 3) Loads the companion of that enclosing class from the macro classloader. * 4) Resolves macro implementation within the loaded companion. * - * @return Some(runtime) if macro implementation can be loaded successfully from either of the mirrors, - * None otherwise. + * @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors, + * null otherwise. */ type MacroRuntime = List[Any] => Any - private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, Option[MacroRuntime]] - private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] = { + private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime] + private def macroRuntime(macroDef: Symbol): MacroRuntime = { macroTraceVerbose("looking for macro implementation: ")(macroDef) if (fastTrack contains macroDef) { macroLogVerbose("macro expansion is serviced by a fast track") - Some(fastTrack(macroDef)) + fastTrack(macroDef) } else { macroRuntimesCache.getOrElseUpdate(macroDef, { - val runtime = { - macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations) - - val ann = macroDef.getAnnotation(MacroImplAnnotation) - if (ann == None) { macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef); return None } - - val macroImpl = ann.get.args(0).symbol - if (macroImpl == NoSymbol) { macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef); return None } - macroLogVerbose("resolved implementation %s at %s".format(macroImpl, macroImpl.pos)) - if (macroImpl.isErroneous) { macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef); return None } - - // [Eugene++] I don't use Scala reflection here, because it seems to interfere with JIT magic - // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20% - // I'm not sure what's the reason - for me it's pure voodoo - def loadMacroImpl(cl: ClassLoader): Option[(Object, jMethod)] = { - try { - // this logic relies on the assumptions that were valid for the old macro prototype - // namely that macro implementations can only be defined in top-level classes and modules - // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different - // for example, a macro def could be defined in a trait that is implemented by an object - // there are some more clever cases when seemingly non-static method ends up being statically accessible - // however, the code below doesn't account for these guys, because it'd take a look of time to get it right - // for now I leave it as a todo and move along to more the important stuff - - macroTraceVerbose("loading implementation class: ")(macroImpl.owner.fullName) - macroTraceVerbose("classloader is: ")(ReflectionUtils.show(cl)) - - // [Eugene] relies on the fact that macro implementations can only be defined in static classes - // [Martin to Eugene++] There's similar logic buried in Symbol#flatname. Maybe we can refactor? - def classfile(sym: Symbol): String = { - def recur(sym: Symbol): String = sym match { - case sym if sym.owner.isPackageClass => - val suffix = if (sym.isModuleClass) "$" else "" - sym.fullName + suffix - case sym => - val separator = if (sym.owner.isModuleClass) "" else "$" - recur(sym.owner) + separator + sym.javaSimpleName.toString - } - - if (sym.isClass || sym.isModule) recur(sym) - else recur(sym.enclClass) - } - - // [Eugene++] this doesn't work for inner classes - // neither does macroImpl.owner.javaClassName, so I had to roll my own implementation - //val receiverName = macroImpl.owner.fullName - val implClassName = classfile(macroImpl.owner) - val implObj = try { - val implObjClass = jClass.forName(implClassName, true, cl) - implObjClass getField "MODULE$" get null - } catch { - case ex: NoSuchFieldException => macroTraceVerbose("exception when loading implObj: ")(ex); null - case ex: NoClassDefFoundError => macroTraceVerbose("exception when loading implObj: ")(ex); null - case ex: ClassNotFoundException => macroTraceVerbose("exception when loading implObj: ")(ex); null - } - - if (implObj == null) None - else { - // [Eugene++] doh, it seems that I need to copy/paste Scala reflection logic - // see `JavaMirrors.methodToJava` or whatever it's called now - val implMeth = { - def typeToJavaClass(tpe: Type): jClass[_] = tpe match { - case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) - case TypeRef(_, ArrayClass, List(elemtpe)) => jArray.newInstance(typeToJavaClass(elemtpe), 0).getClass - case TypeRef(_, sym: ClassSymbol, _) => jClass.forName(classfile(sym), true, cl) - case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") - } - - val paramClasses = transformedType(macroImpl).paramTypes map typeToJavaClass - try implObj.getClass getDeclaredMethod (macroImpl.name.toString, paramClasses: _*) - catch { - case ex: NoSuchMethodException => - val expandedName = - if (macroImpl.isPrivate) nme.expandedName(macroImpl.name.toTermName, macroImpl.owner).toString - else macroImpl.name.toString - implObj.getClass getDeclaredMethod (expandedName, paramClasses: _*) - } - } - macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMeth)) - Some((implObj, implMeth)) - } - } catch { - case ex: ClassNotFoundException => - macroTraceVerbose("implementation class failed to load: ")(ex.toString) - None - case ex: NoSuchMethodException => - macroTraceVerbose("implementation method failed to load: ")(ex.toString) - None - } - } - - loadMacroImpl(macroClassloader) map { - case (implObj, implMeth) => - def runtime(args: List[Any]) = implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*).asInstanceOf[Any] - runtime _ - } + val binding = loadMacroImplBinding(macroDef) + val className = binding.className + val methName = binding.methName + macroLogVerbose(s"resolved implementation as $className.$methName") + + // [Eugene++] I don't use Scala reflection here, because it seems to interfere with JIT magic + // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20% + // I'm not sure what's the reason - for me it's pure voodoo + try { + macroTraceVerbose("loading implementation class: ")(className) + macroTraceVerbose("classloader is: ")(ReflectionUtils.show(macroClassloader)) + val implObj = ReflectionUtils.staticSingletonInstance(macroClassloader, className) + // relies on the fact that macro impls cannot be overloaded + // so every methName can resolve to at maximum one method + val implMeths = implObj.getClass.getDeclaredMethods.find(_.getName == methName) + val implMeth = implMeths getOrElse { throw new NoSuchMethodException(s"$className.$methName") } + macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMeth)) + (args: List[Any]) => implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*) + } catch { + case ex: Exception => + macroTraceVerbose(s"macro runtime failed to load: ")(ex.toString) + macroDef setFlag IS_ERROR + null } - - if (runtime == None) macroDef setFlag IS_ERROR - runtime }) } } @@ -755,7 +830,6 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { */ private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = { val macroDef = expandee.symbol - val runtime = macroRuntime(macroDef) orElse { return None } val prefixTree = expandee.collect{ case Select(qual, name) => qual }.headOption.getOrElse(EmptyTree) val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) var typeArgs = List[Tree]() @@ -771,6 +845,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case _ => } collectMacroArgs(expandee) + + val argcDoesntMatch = macroDef.paramss.length != exprArgs.length + val nullaryArgsEmptyParams = exprArgs.isEmpty && macroDef.paramss == List(List()) + if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee); return None } + var argss: List[List[Any]] = List(context) :: exprArgs.toList macroTraceVerbose("argss: ")(argss) val rawArgss = @@ -787,33 +866,8 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { return None } } else { - val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations))) - val macroImpl = ann.args(0).symbol - var paramss = macroImpl.paramss - val tparams = macroImpl.typeParams - macroTraceVerbose("paramss: ")(paramss) - - // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations - // nullary def + nullary invocation => paramss and argss match, everything is okay - // nullary def + empty-arglist invocation => illegal Scala code, impossible, everything is okay - // empty-paramlist def + nullary invocation => uh-oh, we need to append a List() to argss - // empty-paramlist def + empty-arglist invocation => paramss and argss match, everything is okay - // that's almost it, but we need to account for the fact that paramss might have context bounds that mask the empty last paramlist - val paramss_without_evidences = transformTypeTagEvidenceParams(paramss, (param, tparam) => None) - val isEmptyParamlistDef = paramss_without_evidences.nonEmpty && paramss_without_evidences.last.isEmpty - val isEmptyArglistInvocation = argss.nonEmpty && argss.last.isEmpty - if (isEmptyParamlistDef && !isEmptyArglistInvocation) { - macroLogVerbose("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") - argss = argss :+ Nil - } - - // nb! check partial application against paramss without evidences - val numParamLists = paramss_without_evidences.length - val numArgLists = argss.length - if (numParamLists != numArgLists) { - typer.TyperErrorGen.MacroPartialApplicationError(expandee) - return None - } + val binding = loadMacroImplBinding(macroDef) + macroTraceVerbose("binding: ")(binding) // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences // consider the following example: @@ -831,43 +885,41 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom'' // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim - val resolved = collection.mutable.Map[Symbol, Type]() - paramss = transformTypeTagEvidenceParams(paramss, (param, tparam) => { - val TypeApply(_, implRefTargs) = ann.args(0) - var implRefTarg = implRefTargs(tparam.paramPos).tpe.typeSymbol - val tpe = if (implRefTarg.isTypeParameterOrSkolem) { - if (implRefTarg.owner == macroDef) { + val tags = binding.signature filter (_ != -1) map (paramPos => { + val targ = binding.targs(paramPos).tpe.typeSymbol + val tpe = if (targ.isTypeParameterOrSkolem) { + if (targ.owner == macroDef) { // [Eugene] doesn't work when macro def is compiled separately from its usages - // then implRefTarg is not a skolem and isn't equal to any of macroDef.typeParams - // val paramPos = implRefTarg.deSkolemize.paramPos - val paramPos = macroDef.typeParams.indexWhere(_.name == implRefTarg.name) - typeArgs(paramPos).tpe + // then targ is not a skolem and isn't equal to any of macroDef.typeParams + // val argPos = targ.deSkolemize.paramPos + val argPos = macroDef.typeParams.indexWhere(_.name == targ.name) + typeArgs(argPos).tpe } else - implRefTarg.tpe.asSeenFrom( + targ.tpe.asSeenFrom( if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, macroDef.owner) } else - implRefTarg.tpe - macroLogVerbose("resolved tparam %s as %s".format(tparam, tpe)) - resolved(tparam) = tpe - param.tpe.typeSymbol match { - case definitions.AbsTypeTagClass => - // do nothing - case _ => - throw new Error("unsupported tpe: " + tpe) - } - Some(tparam) + targ.tpe + if (tpe.isConcrete) context.TypeTag(tpe) else context.AbsTypeTag(tpe) + }) + val hasImplicitParams = macroDef.paramss.flatten.lastOption exists (_.isImplicit) + argss = if (hasImplicitParams) argss.dropRight(1) :+ (tags ++ argss.last) else argss :+ tags + + // transforms argss taking into account varargness of paramss + // not all argument lists in argss map to macroDef.paramss, so we need to apply extra care + // namely: + // 1) the first argument list represents (c: Context) in macroImpl, so it doesn't have correspondence in macroDef + // 2) typetag context bounds are only declared on macroImpls, so this optional arglist also doesn't match macroDef + // nb! varargs can apply to any parameter section, not necessarily to the last one + mapWithIndex(argss)((as, i_argss) => { + val i_paramss = i_argss - 1 + val mapsToParamss = 0 <= i_paramss && i_paramss < macroDef.paramss.length + if (mapsToParamss) { + val ps = macroDef.paramss(i_paramss) + if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) + else as + } else as }) - val tags = paramss.last takeWhile (_.isType) map (resolved(_)) map (tpe => if (tpe.isConcrete) context.TypeTag(tpe) else context.AbsTypeTag(tpe)) - if (paramss.lastOption map (params => !params.isEmpty && params.forall(_.isType)) getOrElse false) argss = argss :+ Nil - argss = argss.dropRight(1) :+ (tags ++ argss.last) // todo. add support for context bounds in argss - - assert(argss.length == paramss.length, "argss: %s, paramss: %s".format(argss, paramss)) - val rawArgss = for ((as, ps) <- argss zip paramss) yield { - if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) - else as - } - rawArgss } val rawArgs = rawArgss.flatten macroTraceVerbose("rawArgs: ")(rawArgs) @@ -996,12 +1048,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { return Cancel(typer.infer.setError(expandee)) } - macroRuntime(expandee.symbol) match { - case Some(runtime) => - macroExpandWithRuntime(typer, expandee, runtime) - case None => - macroExpandWithoutRuntime(typer, expandee) - } + val runtime = macroRuntime(expandee.symbol) + if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) + else macroExpandWithoutRuntime(typer, expandee) } /** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index b817ec7b5e..fc9cb02d37 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -556,7 +556,7 @@ trait Namers extends MethodSynthesis { // via "x$lzy" as can be seen in test #3927. val sym = ( if (owner.isClass) createFieldSymbol(tree) - else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, (tree.mods.flags | HIDDEN) & ~IMPLICIT) + else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, (tree.mods.flags | ARTIFACT) & ~IMPLICIT) ) enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) } @@ -577,7 +577,7 @@ trait Namers extends MethodSynthesis { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => assignAndEnterFinishedSymbol(tree) case DefDef(mods, name, tparams, _, _, _) => - val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE | HIDDEN else 0 + val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE | ARTIFACT else 0 val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag if (name == nme.copy && sym.isSynthetic) @@ -988,6 +988,15 @@ trait Namers extends MethodSynthesis { // (either "macro ???" as they used to or just "???" to maximally simplify their compilation) if (fastTrack contains ddef.symbol) ddef.symbol setFlag MACRO + // macro defs need to be typechecked in advance + // because @macroImpl annotation only gets assigned during typechecking + // otherwise macro defs wouldn't be able to robustly coexist with their clients + // because a client could be typechecked before a macro def that it uses + if (ddef.symbol.isTermMacro) { + val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) + typer.computeMacroDefType(ddef, pt) + } + thisMethodType({ val rt = ( if (!tpt.isEmpty) { diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 3bae4d8a46..74acaba74a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -164,7 +164,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$"), qual.pos, newFlags = HIDDEN) setInfo qual.tpe + val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos, newFlags = ARTIFACT) setInfo qual.tpe blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 @@ -281,7 +281,7 @@ trait NamesDefaults { self: Analyzer => } else arg.tpe ).widen // have to widen or types inferred from literal defaults will be singletons - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = HIDDEN) setInfo ( + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = ARTIFACT) setInfo ( if (byName) functionType(Nil, argTpe) else argTpe ) (context.scope.enter(s), byName, repeated) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 1e5f8cb604..b11b327648 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -8,7 +8,7 @@ package scala.tools.nsc package typechecker import symtab._ -import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC, HIDDEN} +import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC, ARTIFACT} import language.postfixOps import scala.tools.nsc.transform.TypingTransformers import scala.tools.nsc.transform.Transform @@ -67,10 +67,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL object exceeded extends Exception { val advice = s"(The analysis required more space than allowed. Please try with scalac -Dscalac.patmat.analysisBudget=${AnalysisBudget.max*2} or -Dscalac.patmat.analysisBudget=off.)" } - - object stackOverflow extends Exception { - val advice = "(There was a stack overflow. Please try increasing the stack available to the compiler using e.g., -Xss2m.)" - } } def newTransformer(unit: CompilationUnit): Transformer = @@ -521,7 +517,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // case Star(_) | ArrayValue => error("stone age pattern relics encountered!") case _ => - error("unsupported pattern: "+ patTree +"(a "+ patTree.getClass +")") + typer.context.unit.error(patTree.pos, s"unsupported pattern: $patTree (a ${patTree.getClass}).\n This is a scalac bug. Tree diagnostics: ${asCompactDebugString(patTree)}.") noFurtherSubPats() } @@ -1147,7 +1143,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC | HIDDEN + val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC | ARTIFACT (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter } @@ -2056,21 +2052,29 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def Lit(sym: Sym, pos: Boolean = true): Lit // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big + // TODO: be smarter/more efficient about this (http://lara.epfl.ch/w/sav09:tseitin_s_encoding) def eqFreePropToSolvable(p: Prop): Formula = { - // TODO: for now, reusing the normalization from DPLL - def negationNormalForm(p: Prop): Prop = p match { - case And(a, b) => And(negationNormalForm(a), negationNormalForm(b)) - case Or(a, b) => Or(negationNormalForm(a), negationNormalForm(b)) - case Not(And(a, b)) => negationNormalForm(Or(Not(a), Not(b))) - case Not(Or(a, b)) => negationNormalForm(And(Not(a), Not(b))) - case Not(Not(p)) => negationNormalForm(p) - case Not(True) => False - case Not(False) => True - case True - | False - | (_ : Sym) - | Not(_ : Sym) => p - } + def negationNormalFormNot(p: Prop, budget: Int = AnalysisBudget.max): Prop = + if (budget <= 0) throw AnalysisBudget.exceeded + else p match { + case And(a, b) => Or(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1)) + case Or(a, b) => And(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1)) + case Not(p) => negationNormalForm(p, budget - 1) + case True => False + case False => True + case s: Sym => Not(s) + } + + def negationNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Prop = + if (budget <= 0) throw AnalysisBudget.exceeded + else p match { + case And(a, b) => And(negationNormalForm(a, budget - 1), negationNormalForm(b, budget - 1)) + case Or(a, b) => Or(negationNormalForm(a, budget - 1), negationNormalForm(b, budget - 1)) + case Not(negated) => negationNormalFormNot(negated, budget - 1) + case True + | False + | (_ : Sym) => p + } val TrueF = formula() val FalseF = formula(clause()) @@ -2113,12 +2117,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } val start = Statistics.startTimer(patmatCNF) - val res = - try { - conjunctiveNormalForm(negationNormalForm(p)) - } catch { case ex : StackOverflowError => - throw AnalysisBudget.stackOverflow - } + val res = conjunctiveNormalForm(negationNormalForm(p)) Statistics.stopTimer(patmatCNF, start) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index d0fbc7043e..3507bc4e09 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -172,7 +172,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def varargBridge(member: Symbol, bridgetpe: Type): Tree = { log("Generating varargs bridge for " + member.fullLocationString + " of type " + bridgetpe) - val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE | HIDDEN) setPos clazz.pos + val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE | ARTIFACT) setPos clazz.pos bridge.setInfo(bridgetpe.cloneInfo(bridge)) clazz.info.decls enter bridge diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 5e511591f3..2a71295690 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -61,7 +61,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val supername = nme.superName(name) val superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) orElse { debuglog(s"add super acc ${sym.fullLocationString} to $clazz") - val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE | HIDDEN) setAlias sym + val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE | ARTIFACT) setAlias sym val tpe = clazz.thisType memberType sym match { case t if sym.isModule && !sym.isMethod => NullaryMethodType(t) case t => t @@ -387,7 +387,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } val protAcc = clazz.info.decl(accName).suchThat(s => s == NoSymbol || s.tpe =:= accType(s)) orElse { - val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos, newFlags = HIDDEN) + val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos, newFlags = ARTIFACT) newAcc setInfoAndEnter accType(newAcc) val code = DefDef(newAcc, { @@ -449,7 +449,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val accName = nme.protSetterName(field.originalName) val protectedAccessor = clazz.info decl accName orElse { - val protAcc = clazz.newMethod(accName, field.pos, newFlags = HIDDEN) + val protAcc = clazz.newMethod(accName, field.pos, newFlags = ARTIFACT) val paramTypes = List(clazz.typeOfThis, field.tpe) val params = protAcc newSyntheticValueParams paramTypes val accessorType = MethodType(params, UnitClass.tpe) diff --git a/src/ensime/.ensime.SAMPLE b/src/ensime/.ensime.SAMPLE new file mode 100644 index 0000000000..10801816b7 --- /dev/null +++ b/src/ensime/.ensime.SAMPLE @@ -0,0 +1,17 @@ +( + :disable-source-load-on-startup t + :disable-scala-jars-on-classpath t + :root-dir "c:/Projects/Kepler" + :sources ( + "c:/Projects/Kepler/src/library" + "c:/Projects/Kepler/src/reflect" + "c:/Projects/Kepler/src/compiler" + ) + :compile-deps ( + "c:/Projects/Kepler/build/asm/classes" + "c:/Projects/Kepler/build/locker/classes/library" + "c:/Projects/Kepler/build/locker/classes/reflect" + "c:/Projects/Kepler/build/locker/classes/compiler" + ) + :target "c:/Projects/Kepler/build/classes" +)
\ No newline at end of file diff --git a/src/ensime/README.md b/src/ensime/README.md new file mode 100644 index 0000000000..302d47b8a7 --- /dev/null +++ b/src/ensime/README.md @@ -0,0 +1,11 @@ +Ensime project files +===================== + +Rename .ensime.SAMPLE to .ensime and replace sample paths with real paths to your sources and build results. +After that you're good to go with one of the ENSIME-enabled text editors. + +Editors that know how to talk to ENSIME servers: +1) Emacs via https://github.com/aemoncannon/ensime +2) jEdit via https://github.com/djspiewak/ensime-sidekick +3) TextMate via https://github.com/mads379/ensime.tmbundle +4) Sublime Text 2 via https://github.com/sublimescala/sublime-ensime diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index 81db2d583a..3388d584db 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -92,6 +92,8 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ i - len } + override /*IterableLike*/ def isEmpty: Boolean = lengthCompare(0) == 0 + /** The size of this $coll, equivalent to `length`. * * $willNotTerminateInf diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index fda76c7b95..3415b0fa10 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -67,6 +67,11 @@ trait Symbols extends base.Symbols { self: Universe => */ def isSynthetic: Boolean + /** Does this symbol represent an implementation artifact that isn't meant for public use? + * Examples of such artifacts are erasure bridges and $outer fields. + */ + def isImplementationArtifact: Boolean + /** Does this symbol represent a local declaration or definition? * * If yes, either `isPrivate` or `isProtected` are guaranteed to be true. diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala index 0a848f8d67..906dfb95fb 100644 --- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -342,7 +342,7 @@ object ClassfileConstants { case JAVA_ACC_PRIVATE => PRIVATE case JAVA_ACC_PROTECTED => PROTECTED case JAVA_ACC_FINAL => FINAL - case JAVA_ACC_SYNTHETIC => SYNTHETIC | HIDDEN // maybe should be just hidden? + case JAVA_ACC_SYNTHETIC => SYNTHETIC | ARTIFACT // maybe should be just artifact? case JAVA_ACC_STATIC => STATIC case JAVA_ACC_ABSTRACT => if (isAnnotation) 0L else if (isClass) ABSTRACT else DEFERRED case JAVA_ACC_INTERFACE => if (isAnnotation) 0L else TRAIT | INTERFACE | ABSTRACT @@ -372,7 +372,7 @@ object ClassfileConstants { } def methodFlags(jflags: Int): Long = { initFields(jflags) - translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE | HIDDEN else 0) + translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE | ARTIFACT else 0) } } object FlagTranslation extends FlagTranslation { } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 86355bbb7f..f01247d918 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -852,8 +852,8 @@ trait Definitions extends api.StandardDefinitions { lazy val Object_!= = enterNewMethod(ObjectClass, nme.NE, anyrefparam, booltype, FINAL) lazy val Object_eq = enterNewMethod(ObjectClass, nme.eq, anyrefparam, booltype, FINAL) lazy val Object_ne = enterNewMethod(ObjectClass, nme.ne, anyrefparam, booltype, FINAL) - lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC | HIDDEN)(_ => booltype) - lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC | HIDDEN)(_.typeConstructor) + lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC | ARTIFACT)(_ => booltype) + lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC | ARTIFACT)(_.typeConstructor) lazy val Object_synchronized = newPolyMethod(1, ObjectClass, nme.synchronized_, FINAL)(tps => (Some(List(tps.head.typeConstructor)), tps.head.typeConstructor) ) diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 3f4f24a9f4..f021a9eebd 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -116,9 +116,9 @@ class ModifierFlags { final val LAZY = 1L << 31 // symbol is a lazy val. can't have MUTABLE unless transformed by typer final val PRESUPER = 1L << 37 // value is evaluated before super call final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit - final val HIDDEN = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode + final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode - /** Symbols which are marked HIDDEN. (Expand this list?) + /** Symbols which are marked ARTIFACT. (Expand this list?) * * - $outer fields and accessors * - super accessors @@ -128,7 +128,7 @@ class ModifierFlags { * - default argument getters * - evaluation-order preserving locals for right-associative and out-of-order named arguments * - catch-expression storing vals - * - anything else which feels a setFlag(HIDDEN) + * - anything else which feels a setFlag(ARTIFACT) */ // Overridden. @@ -149,7 +149,7 @@ class Flags extends ModifierFlags { final val CAPTURED = 1 << 16 // variable is accessed from nested function. Set by LambdaLift. final val LABEL = 1 << 17 // method symbol is a label. Set by TailCall final val INCONSTRUCTOR = 1 << 17 // class symbol is defined in this/superclass constructor. - final val SYNTHETIC = 1 << 21 // symbol is compiler-generated (compare with HIDDEN) + final val SYNTHETIC = 1 << 21 // symbol is compiler-generated (compare with ARTIFACT) final val STABLE = 1 << 22 // functions that are assumed to be stable // (typically, access methods for valdefs) // or classes that do not contain abstract types. @@ -231,7 +231,7 @@ class Flags extends ModifierFlags { /** To be a little clearer to people who aren't habitual bit twiddlers. */ final val AllFlags = -1L - + /** These flags can be set when class or module symbol is first created. * They are the only flags to survive a call to resetFlags(). */ @@ -261,7 +261,7 @@ class Flags extends ModifierFlags { /** These modifiers appear in TreePrinter output. */ final val PrintableFlags = ExplicitFlags | BridgeFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO | - ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | HIDDEN + ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT /** When a symbol for a field is created, only these flags survive * from Modifiers. Others which may be applied at creation time are: @@ -301,11 +301,11 @@ class Flags extends ModifierFlags { /** These flags are not pickled */ final val FlagsNotPickled = IS_ERROR | OVERLOADED | LIFTED | TRANS_FLAG | LOCKED | TRIEDCOOKING - + // A precaution against future additions to FlagsNotPickled turning out // to be overloaded flags thus not-pickling more than intended. assert((OverloadedFlagsMask & FlagsNotPickled) == 0, flagsToString(OverloadedFlagsMask & FlagsNotPickled)) - + /** These flags are pickled */ final val PickledFlags = InitialFlags & ~FlagsNotPickled @@ -352,13 +352,13 @@ class Flags extends ModifierFlags { (SEALED, SEALED_PKL), (ABSTRACT, ABSTRACT_PKL) ) - + private val mappedRawFlags = rawPickledCorrespondence map (_._1) private val mappedPickledFlags = rawPickledCorrespondence map (_._2) - + private class MapFlags(from: Array[Long], to: Array[Long]) extends (Long => Long) { val fromSet = (0L /: from) (_ | _) - + def apply(flags: Long): Long = { var result = flags & ~fromSet var tobeMapped = flags & fromSet @@ -373,7 +373,7 @@ class Flags extends ModifierFlags { result } } - + val rawToPickledFlags: Long => Long = new MapFlags(mappedRawFlags, mappedPickledFlags) val pickledToRawFlags: Long => Long = new MapFlags(mappedPickledFlags, mappedRawFlags) @@ -427,7 +427,7 @@ class Flags extends ModifierFlags { case VARARGS => "<varargs>" // (1L << 43) case TRIEDCOOKING => "<triedcooking>" // (1L << 44) case SYNCHRONIZED => "<synchronized>" // (1L << 45) - case HIDDEN => "<hidden>" // (1L << 46) + case ARTIFACT => "<artifact>" // (1L << 46) case 0x800000000000L => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) case 0x2000000000000L => "" // (1L << 49) @@ -447,7 +447,7 @@ class Flags extends ModifierFlags { case 0x8000000000000000L => "" // (1L << 63) case _ => "" } - + private def accessString(flags: Long, privateWithin: String)= ( if (privateWithin == "") { if ((flags & PrivateLocal) == PrivateLocal) "private[this]" @@ -459,7 +459,7 @@ class Flags extends ModifierFlags { else if ((flags & PROTECTED) != 0) "protected[" + privateWithin + "]" else "private[" + privateWithin + "]" ) - + @deprecated("Use flagString on the flag-carrying member", "2.10.0") def flagsToString(flags: Long, privateWithin: String): String = { val access = accessString(flags, privateWithin) diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala index 7ead9d6a1b..62c8ed702b 100644 --- a/src/reflect/scala/reflect/internal/HasFlags.scala +++ b/src/reflect/scala/reflect/internal/HasFlags.scala @@ -66,7 +66,7 @@ trait HasFlags { */ def flagString: String = flagString(flagMask) def flagString(mask: Long): String = calculateFlagString(flags & mask) - + /** The default mask determining which flags to display. */ def flagMask: Long = AllFlags @@ -92,7 +92,7 @@ trait HasFlags { def isCaseAccessor = hasFlag(CASEACCESSOR) def isDeferred = hasFlag(DEFERRED) def isFinal = hasFlag(FINAL) - def isHidden = hasFlag(HIDDEN) + def isArtifact = hasFlag(ARTIFACT) def isImplicit = hasFlag(IMPLICIT) def isInterface = hasFlag(INTERFACE) def isJavaDefined = hasFlag(JAVA) @@ -136,7 +136,7 @@ trait HasFlags { def accessString: String = { val pw = if (hasAccessBoundary) privateWithin.toString else "" - + if (pw == "") { if (hasAllFlags(PrivateLocal)) "private[this]" else if (hasAllFlags(ProtectedLocal)) "protected[this]" @@ -150,7 +150,7 @@ trait HasFlags { protected def calculateFlagString(basis: Long): String = { val access = accessString val nonAccess = flagBitsToString(basis & ~AccessFlags) - + if (access == "") nonAccess else if (nonAccess == "") access else nonAccess + " " + access diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index e8a011d4f9..5e83f638a2 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -66,6 +66,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isExistential: Boolean = this.isExistentiallyBound def isParamWithDefault: Boolean = this.hasDefault def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM) + def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT) def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) @@ -698,13 +699,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Is this symbol an accessor method for outer? */ final def isOuterAccessor = { - hasFlag(STABLE | HIDDEN) && + hasFlag(STABLE | ARTIFACT) && originalName == nme.OUTER } /** Is this symbol an accessor method for outer? */ final def isOuterField = { - hasFlag(HIDDEN) && + hasFlag(ARTIFACT) && originalName == nme.OUTER_LOCAL } diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 51dc2567f8..fa4a35cfe9 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -372,6 +372,13 @@ abstract class TreeInfo { case _ => EmptyTree } + /** If this tree represents a type application the type arguments. Otherwise Nil. + */ + def typeArguments(tree: Tree): List[Tree] = tree match { + case TypeApply(_, targs) => targs + case _ => Nil + } + /** If this tree has type parameters, those. Otherwise Nil. */ def typeParameters(tree: Tree): List[TypeDef] = tree match { diff --git a/test/files/neg/macro-invalidimpl-i.check b/test/files/neg/macro-invalidimpl-i.check new file mode 100644 index 0000000000..b6277809a3 --- /dev/null +++ b/test/files/neg/macro-invalidimpl-i.check @@ -0,0 +1,4 @@ +Macros_Test_2.scala:4: error: macro implementation must be public
+ def foo = macro Impls.impl
+ ^
+one error found
diff --git a/test/files/neg/macro-invalidimpl-i.flags b/test/files/neg/macro-invalidimpl-i.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/neg/macro-invalidimpl-i.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/neg/macro-invalidimpl-i/Impls_1.scala b/test/files/neg/macro-invalidimpl-i/Impls_1.scala new file mode 100644 index 0000000000..c35d8ab3c1 --- /dev/null +++ b/test/files/neg/macro-invalidimpl-i/Impls_1.scala @@ -0,0 +1,7 @@ +package foo + +import scala.reflect.macros.Context + +object Impls { + private[foo] def impl(c: Context) = ??? +}
\ No newline at end of file diff --git a/test/files/neg/macro-invalidimpl-i/Macros_Test_2.scala b/test/files/neg/macro-invalidimpl-i/Macros_Test_2.scala new file mode 100644 index 0000000000..fb129c70be --- /dev/null +++ b/test/files/neg/macro-invalidimpl-i/Macros_Test_2.scala @@ -0,0 +1,5 @@ +package foo + +object Test extends App { + def foo = macro Impls.impl +} diff --git a/test/files/run/t2886.check b/test/files/run/t2886.check index b093815562..cb0db8a6dc 100644 --- a/test/files/run/t2886.check +++ b/test/files/run/t2886.check @@ -1,5 +1,5 @@ ((x: String) => { - <hidden> val x$1 = x; - <hidden> val x$2 = x; + <artifact> val x$1 = x; + <artifact> val x$2 = x; Test.this.test(x$2, x$1) }) diff --git a/test/files/run/t5940.scala b/test/files/run/t5940.scala new file mode 100644 index 0000000000..147ff38256 --- /dev/null +++ b/test/files/run/t5940.scala @@ -0,0 +1,41 @@ +import scala.tools.partest._ + +object Test extends DirectTest { + def code = ??? + + def macros_1 = """ + import scala.reflect.macros.Context + + object Impls { + def impl(c: Context) = c.literalUnit + } + + object Macros { + //import Impls._ + def impl(c: Context) = c.literalUnit + def foo = macro impl + } + """ + def compileMacros() = { + val classpath = List(sys.props("partest.lib"), sys.props("partest.reflect")) mkString sys.props("path.separator") + compileString(newCompiler("-language:experimental.macros", "-cp", classpath, "-d", testOutput.path))(macros_1) + } + + def test_2 = """ + object Test extends App { + println(Macros.foo) + } + """ + def compileTest() = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(test_2) + } + + def show(): Unit = { + log("Compiling Macros_1...") + if (compileMacros()) { + log("Compiling Test_2...") + if (compileTest()) log("Success!") else log("Failed...") + } + } +}
\ No newline at end of file diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index c10bd76500..42921ae2f5 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -31,14 +31,14 @@ package <empty> { }; final def apply(): Int = $anonfun$foo$1.this.apply$mcI$sp(); <specialized> def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.T$$classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); - <synthetic> <paramaccessor> <hidden> private[this] val $outer: T = _; - <synthetic> <stable> <hidden> def T$$anonfun$$$outer(): T = $anonfun$foo$1.this.$outer; - final <bridge> <hidden> def apply(): Object = scala.Int.box($anonfun$foo$1.this.apply()); + <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _; + <synthetic> <stable> <artifact> def T$$anonfun$$$outer(): T = $anonfun$foo$1.this.$outer; + final <bridge> <artifact> def apply(): Object = scala.Int.box($anonfun$foo$1.this.apply()); <synthetic> <paramaccessor> private[this] val methodParam$1: Int = _; <synthetic> <paramaccessor> private[this] val methodLocal$1: Int = _ }; abstract trait MethodLocalTrait$1 extends Object { - <synthetic> <stable> <hidden> def T$MethodLocalTrait$$$outer(): T + <synthetic> <stable> <artifact> def T$MethodLocalTrait$$$outer(): T }; object MethodLocalObject$2 extends Object with T#MethodLocalTrait$1 { def <init>($outer: T, barParam$1: Int): T#MethodLocalObject$2.type = { @@ -46,9 +46,9 @@ package <empty> { MethodLocalObject$2.this.$asInstanceOf[T#MethodLocalTrait$1$class]()./*MethodLocalTrait$1$class*/$init$(barParam$1); () }; - <synthetic> <paramaccessor> <hidden> private[this] val $outer: T = _; - <synthetic> <stable> <hidden> def T$MethodLocalObject$$$outer(): T = MethodLocalObject$2.this.$outer; - <synthetic> <stable> <hidden> def T$MethodLocalTrait$$$outer(): T = MethodLocalObject$2.this.$outer + <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _; + <synthetic> <stable> <artifact> def T$MethodLocalObject$$$outer(): T = MethodLocalObject$2.this.$outer; + <synthetic> <stable> <artifact> def T$MethodLocalTrait$$$outer(): T = MethodLocalObject$2.this.$outer }; final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1); @@ -69,9 +69,9 @@ package <empty> { <specialized> def apply$mcV$sp(): Unit = try { $anonfun$tryy$1.this.tryyLocal$1.elem = $anonfun$tryy$1.this.tryyParam$1 } finally (); - <synthetic> <paramaccessor> <hidden> private[this] val $outer: T = _; - <synthetic> <stable> <hidden> def T$$anonfun$$$outer(): T = $anonfun$tryy$1.this.$outer; - final <bridge> <hidden> def apply(): Object = { + <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _; + <synthetic> <stable> <artifact> def T$$anonfun$$$outer(): T = $anonfun$tryy$1.this.$outer; + final <bridge> <artifact> def apply(): Object = { $anonfun$tryy$1.this.apply(); scala.runtime.BoxedUnit.UNIT }; diff --git a/test/scaladoc/run/implicits-known-type-classes.scala b/test/scaladoc/run/implicits-known-type-classes.scala index 9f4ca372b0..471a1a219d 100644 --- a/test/scaladoc/run/implicits-known-type-classes.scala +++ b/test/scaladoc/run/implicits-known-type-classes.scala @@ -24,7 +24,7 @@ object Test extends ScaladocModelTest { val A = base._class("A") - for (conversion <- A.conversions if !conversion.isCommonConversion) { + for (conversion <- A.conversions if !conversion.isHiddenConversion) { assert(conversion.constraints.length == 1, conversion.constraints.length + " == 1 (in " + conversion + ")") assert(conversion.constraints.head.isInstanceOf[KnownTypeClassConstraint], conversion.constraints.head + " is not a known type class constraint!") |