New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@getter StaticAnnotations on ValDefs are nondeterminstically visible to macros depending on order of typechecking #7561
Comments
Imported From: https://issues.scala-lang.org/browse/SI-7561?orig=1 |
@gkossakowski said: |
@xeno-by said: |
@gkossakowski said: Also, I saw #7424 but suggested work-around doesn't help in this case. |
@xeno-by said: |
Emre Çelikten (emre) said: |
@retronym said (edited on Oct 26, 2013 1:56:56 PM UTC): /code/gist/5724506-SI-7561 tail -n 1000 *.scala
==> Consumer.scala <==
package cons
import mac._
class Bar {
@fancy val k: Int = 5
def k$fancy: Int = ???
}
class Consumer {
val f: Foo = new Foo
val b: Bar = new Bar
Rewrite.rewrite(f.k)
Rewrite.rewrite(b.k)
}
class Foo {
@fancy val k: Int = 5
def k$fancy: Int = ???
}
==> Rewrite.scala <==
package mac
@scala.annotation.meta.getter
class fancy extends scala.annotation.StaticAnnotation
object Rewrite {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def rewrite[A](a: A) = macro rewriteImpl[A]
def rewriteImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]): c.Expr[A] = {
import c.universe._
def accessedOrSelf(s: Symbol) = if (s.isTerm) s.asTerm.accessed orElse s else s
// workaround for SI-7561. The namer phase in Scalac creates a getter symbol
// derived from the primary symbol (the field) of a ValDef tree. Only the primary
// symbol reads annotations in its type completer.
//
// When typechecking the enclosing template, the annotations from the primary
// symbol are copied or moved to the derived symbols, based on meta-annotations
// like @getter. But a macro could easily observe the symbols before this stage
// and see a getter without annotations.
//
// This method searchs all annotations on the given symbol (perhaps an accessor)
// and its underlying field for one that matches `f`
def isAnnotated(s: Symbol)(f: AnnotationApi => Boolean) = {
def check(s: Symbol) = s.annotations.exists(f)
Set(s, accessedOrSelf(s)).exists(s => check(s))
}
val fancySym = weakTypeOf[fancy].typeSymbol
def isFancy(a: AnnotationApi) = a.tpe.typeSymbol == fancySym
val t = a.tree match {
case sel @ Select(qual, name) if isAnnotated(sel.symbol)(isFancy) =>
val result = Select(qual, newTermName(name + "$fancy"))
println(s"rewriting $sel to $result")
result
case s => s
}
c.Expr[A](t)
}
}
|
@retronym said: git diff -U8 -- src/compiler/scala/tools/nsc/typechecker/Namers.scala
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index e3d7bfd..451cfab 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -766,16 +766,17 @@ trait Namers extends MethodSynthesis {
/* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym =>
logAndValidate(sym) {
sym setInfo {
val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitTpe)
else NullaryMethodType(typeSig(tree))
pluginsTypeSigAccessor(tp, typer, tree, sym)
}
+ sym.setAnnotations(tree.symbol.annotations)
}
}
def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym =>
val selftpe = typer.typedType(tree).tpe
sym setInfo {
if (selftpe.typeSymbol isNonBottomSubClass sym.owner) selftpe
else intersectionType(List(sym.owner.tpe, selftpe)) This patch just blindly copies all annotations to the getter symbol in the getter's type completer. (Actually, it just shares references). The question will be whether we can do the necessary filtering here (based on meta-annotations) without risking triggering cycles. |
@dragos said: |
@michael72 said: |
See attached. The behaviour of the rewrite macro is dependent on the order of class Foo and object Consumer in Consumer.scala. If you put object Consumer before class Foo the error will disappear as the macro does not see the annotation.
In the macro code you can see an attempt to force potentially (uninitialized) infos of annotations. However, that doesn't help because annotations list is empty.
Also note that if you change annotated val
k
to be def then the problem disappears.The text was updated successfully, but these errors were encountered: