summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-11-06 22:23:09 +0100
committerJason Zaugg <jzaugg@gmail.com>2012-11-06 22:31:50 +0100
commit6902da3168c02448387edc000dedfe97ef5f7cd9 (patch)
tree4110e85872ce34f79c73bef7e5b6a2e3130b0a8f
parent3b68b45a200087104a1ac2de7c4b86635023fd4d (diff)
downloadscala-6902da3168c02448387edc000dedfe97ef5f7cd9.tar.gz
scala-6902da3168c02448387edc000dedfe97ef5f7cd9.tar.bz2
scala-6902da3168c02448387edc000dedfe97ef5f7cd9.zip
SI-6539 Annotation for methods unfit for post-typer ASTs
Motivated by the `.value` method in the SBT task-syntax branch, which should only be called within the context of the argument to a setting initialization macro. The facility is akin to a fatal deprecation.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala6
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala1
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala3
-rw-r--r--src/reflect/scala/reflect/macros/compileTimeOnly.scala16
-rw-r--r--test/files/neg/t6539.check10
-rw-r--r--test/files/neg/t6539/Macro_1.scala10
-rw-r--r--test/files/neg/t6539/Test_2.scala6
7 files changed, 52 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index c1dc91dbfc..726c8d3b24 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1376,6 +1376,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
)
}
+ private def checkCompileTimeOnly(sym: Symbol, pos: Position) = {
+ if (sym.isCompileTimeOnly)
+ unit.error(pos, s"reference to ${sym.fullLocationString} should not survive typechecking: ${sym.compileTimeOnlyMessage.get}")
+ }
+
private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = (
(otherSym != NoSymbol)
&& !otherSym.isProtected
@@ -1562,6 +1567,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
checkDeprecated(sym, tree.pos)
if (settings.Xmigration28.value)
checkMigration(sym, tree.pos)
+ checkCompileTimeOnly(sym, tree.pos)
if (sym eq NoSymbol) {
unit.warning(tree.pos, "Select node has NoSymbol! " + tree + " / " + tree.tpe)
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index e24971a309..4c7694c319 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -944,6 +944,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val BeanPropertyAttr = requiredClass[scala.beans.BeanProperty]
lazy val BooleanBeanPropertyAttr = requiredClass[scala.beans.BooleanBeanProperty]
lazy val CloneableAttr = requiredClass[scala.annotation.cloneable]
+ lazy val CompileTimeOnlyAttr = requiredClass[scala.reflect.macros.compileTimeOnly]
lazy val DeprecatedAttr = requiredClass[scala.deprecated]
lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName]
lazy val DeprecatedInheritanceAttr = requiredClass[scala.deprecatedInheritance]
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 7cb9a0e105..7a0f5f2caf 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -743,6 +743,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def elisionLevel = getAnnotation(ElidableMethodClass) flatMap { _.intArg(0) }
def implicitNotFoundMsg = getAnnotation(ImplicitNotFoundClass) flatMap { _.stringArg(0) }
+ def isCompileTimeOnly = hasAnnotation(CompileTimeOnlyAttr)
+ def compileTimeOnlyMessage = getAnnotation(CompileTimeOnlyAttr) flatMap (_ stringArg 0)
+
/** Is this symbol an accessor method for outer? */
final def isOuterAccessor = {
hasFlag(STABLE | ARTIFACT) &&
diff --git a/src/reflect/scala/reflect/macros/compileTimeOnly.scala b/src/reflect/scala/reflect/macros/compileTimeOnly.scala
new file mode 100644
index 0000000000..4018e7db71
--- /dev/null
+++ b/src/reflect/scala/reflect/macros/compileTimeOnly.scala
@@ -0,0 +1,16 @@
+package scala.reflect
+package macros
+
+import scala.annotation.meta._
+
+/**
+ * An annotation that designates a member should not be referred to after
+ * type checking (which includes macro expansion); it must only be used in
+ * the arguments of some other macro that will eliminate it from the AST.
+ *
+ * @param message the error message to print during compilation if a reference remains
+ * after type checking
+ * @since 2.10.0
+ */
+@getter @setter @beanGetter @beanSetter
+final class compileTimeOnly(message: String = "") extends scala.annotation.StaticAnnotation
diff --git a/test/files/neg/t6539.check b/test/files/neg/t6539.check
new file mode 100644
index 0000000000..a5d5a7244d
--- /dev/null
+++ b/test/files/neg/t6539.check
@@ -0,0 +1,10 @@
+Test_2.scala:2: error: reference to method cto in object M should not survive typechecking: cto may only be used as an argument to m
+ M.cto // error
+ ^
+Test_2.scala:3: error: reference to method cto in object M should not survive typechecking: cto may only be used as an argument to m
+ M.m(M.cto, ()) // error
+ ^
+Test_2.scala:5: error: reference to method cto in object M should not survive typechecking: cto may only be used as an argument to m
+ M.cto // error
+ ^
+three errors found
diff --git a/test/files/neg/t6539/Macro_1.scala b/test/files/neg/t6539/Macro_1.scala
new file mode 100644
index 0000000000..69bd53fe07
--- /dev/null
+++ b/test/files/neg/t6539/Macro_1.scala
@@ -0,0 +1,10 @@
+import language.experimental.macros
+import reflect.macros.Context
+
+object M {
+ def m(a: Any, b: Any): Any = macro mImpl
+ def mImpl(c: Context)(a: c.Expr[Any], b: c.Expr[Any]) = a
+
+ @reflect.macros.compileTimeOnly("cto may only be used as an argument to m")
+ def cto = 0
+}
diff --git a/test/files/neg/t6539/Test_2.scala b/test/files/neg/t6539/Test_2.scala
new file mode 100644
index 0000000000..1569c79788
--- /dev/null
+++ b/test/files/neg/t6539/Test_2.scala
@@ -0,0 +1,6 @@
+object Test {
+ M.cto // error
+ M.m(M.cto, ()) // error
+ M.m((), M.cto) // okay
+ M.cto // error
+} \ No newline at end of file