Skip to content

Commit

Permalink
SI-5744 evidence params are now SYNTHETIC
Browse files Browse the repository at this point in the history
The macro def <-> macro impl correspondence check compares names of the
corresponding parameters in def and impl and reports an error if they
don't match. This was originally designed to avoid confusion w.r.t named
arguments (which ended up being never implemented as described in SI-5920).

Sometimes parameter names are generated by the compiler, which puts the
user in a tough position. Luckily, there's an escape hatch built it, which
omits the name correspondence check if one of the parameters is SYNTHETIC.
Everything went well until we realized that evidences generated by
context bounds aren't SYNTHETIC, which led to the bug at hand.

Marking auto-generated evidence parameters SYNTHETIC was only the first
step, as the correspondence checker uses parameter symbols, not parameter
trees. Why's that a problem? Because SYNTHETIC doesn't get propagated from def
trees to their underlying symbols (see ValueParameterFlags).

Unfortunately one cannot just change ValueParameterFlags, because that
would break printouts generated in TypeDiagnostics, which is designed to not
print synthetic symbols. Thus we modify methodTypeErrorString in
TypeDiagnostics to always print synthetic symbols.

Therefore now we propagate all paramSym.flags when doing correspondent sweeps
to keep them in sync between def trees and their underlying symbols. This
fixes the problem.
  • Loading branch information
u-abramchuk committed Feb 16, 2013
1 parent 466fc67 commit 9d5d55b
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
Expand Up @@ -589,7 +589,7 @@ abstract class TreeBuilder {
if (contextBounds.isEmpty) vparamss
else {
val mods = Modifiers(if (owner.isTypeName) PARAMACCESSOR | LOCAL | PRIVATE else PARAM)
def makeEvidenceParam(tpt: Tree) = ValDef(mods | IMPLICIT, freshTermName(nme.EVIDENCE_PARAM_PREFIX), tpt, EmptyTree)
def makeEvidenceParam(tpt: Tree) = ValDef(mods | IMPLICIT | SYNTHETIC, freshTermName(nme.EVIDENCE_PARAM_PREFIX), tpt, EmptyTree)
val evidenceParams = contextBounds map makeEvidenceParam

val vparamssLast = if(vparamss.nonEmpty) vparamss.last else Nil
Expand Down
4 changes: 1 addition & 3 deletions src/compiler/scala/tools/nsc/typechecker/Macros.scala
Expand Up @@ -333,9 +333,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
def param(tree: Tree): Symbol =
paramCache.getOrElseUpdate(tree.symbol, {
val sym = tree.symbol
val sigParam = makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe))
if (sym.isSynthetic) sigParam.flags |= SYNTHETIC
sigParam
makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe), sym.flags)
})

val paramss = List(ctxParam) :: mmap(vparamss)(param)
Expand Down
Expand Up @@ -111,14 +111,9 @@ trait TypeDiagnostics {
"\n(Note that variables need to be initialized to be defined)"
else ""

/** Only prints the parameter names if they're not synthetic,
* since "x$1: Int" does not offer any more information than "Int".
*/
private def methodTypeErrorString(tp: Type) = tp match {
case mt @ MethodType(params, resultType) =>
def forString =
if (params exists (_.isSynthetic)) params map (_.tpe)
else params map (_.defString)
def forString = params map (_.defString)

forString.mkString("(", ",", ")") + resultType
case x => x.toString
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/Flags.scala
Expand Up @@ -274,7 +274,7 @@ class Flags extends ModifierFlags {
* from Modifiers. Others which may be applied at creation time are:
* SYNTHETIC.
*/
final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM | STABLE
final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM | STABLE | SYNTHETIC
final val BeanPropertyFlags = DEFERRED | OVERRIDE | STATIC
final val VarianceFlags = COVARIANT | CONTRAVARIANT

Expand Down
22 changes: 22 additions & 0 deletions test/files/pos/t5744/Macros_1.scala
@@ -0,0 +1,22 @@
import scala.language.experimental.macros
import scala.reflect.macros.Context

object Macros {
def foo[U: Numeric](x: U) = macro foo_impl[U]
def bar[U: Numeric : Equiv, Y <% String](x: U)(implicit s: String) = macro bar_impl[U, Y]

def foo_impl[U](c: Context)(x: c.Expr[U])(numeric: c.Expr[Numeric[U]]) = {
import c.universe._
val plusOne = Apply(Select(numeric.tree, newTermName("plus")), List(x.tree, Literal(Constant(1))))
val body = Apply(Select(Ident(definitions.PredefModule), newTermName("println")), List(plusOne))
c.Expr[Unit](body)
}

def bar_impl[U, Y](c: Context)(x: c.Expr[U])(numeric: c.Expr[Numeric[U]], equiv: c.Expr[Equiv[U]], viewAsString: c.Expr[Y => String], s: c.Expr[String]) = {
import c.universe._
val plusOne = Apply(Select(numeric.tree, newTermName("plus")), List(x.tree, Literal(Constant(1))))
val plusLen = Apply(Select(numeric.tree, newTermName("plus")), List(plusOne, Select(s.tree, newTermName("length"))))
val body = Apply(Select(Ident(definitions.PredefModule), newTermName("println")), List(plusLen))
c.Expr[Unit](body)
}
}
6 changes: 6 additions & 0 deletions test/files/pos/t5744/Test_2.scala
@@ -0,0 +1,6 @@
object Test extends App {
import Macros._
foo(42)
implicit val s = ""
bar(43)
}

0 comments on commit 9d5d55b

Please sign in to comment.