From 1b5f73129fc2f678d00905e5d851536251f8821a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 10 Aug 2013 14:51:34 +0200 Subject: moves compileTimeOnly to scala-library This is the notion that's come to be universally useful, so I suggest we promote it to be universally accessible. Note that the attached test incorrectly fails to report errors for definitions coming from the empty package and for annotations. These are bugs, and they are fixed in subsequent commits of this pull request. --- src/library/scala/annotation/compileTimeOnly.scala | 22 +++++++++ .../scala/reflect/internal/Definitions.scala | 2 +- .../internal/annotations/compileTimeOnly.scala | 32 ------------ .../reflect/internal/annotations/package.scala | 6 +++ test/files/neg/compile-time-only-a.check | 19 ++++++++ test/files/neg/compile-time-only-a.scala | 57 ++++++++++++++++++++++ 6 files changed, 105 insertions(+), 33 deletions(-) create mode 100644 src/library/scala/annotation/compileTimeOnly.scala delete mode 100644 src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala create mode 100644 src/reflect/scala/reflect/internal/annotations/package.scala create mode 100644 test/files/neg/compile-time-only-a.check create mode 100644 test/files/neg/compile-time-only-a.scala diff --git a/src/library/scala/annotation/compileTimeOnly.scala b/src/library/scala/annotation/compileTimeOnly.scala new file mode 100644 index 0000000000..942e9cad8c --- /dev/null +++ b/src/library/scala/annotation/compileTimeOnly.scala @@ -0,0 +1,22 @@ +package scala.annotation + +import scala.annotation.meta._ + +/** + * An annotation that designates that an annottee should not be referred to after + * type checking (which includes macro expansion). + * + * Examples of potential use: + * 1) The annottee can only appear in the arguments of some other macro + * that will eliminate it from the AST during expansion. + * 2) The annottee is a macro and should have been expanded away, + * so if hasn't, something wrong has happened. + * (Comes in handy to provide better support for new macro flavors, + * e.g. macro annotations, that can't be expanded by the vanilla compiler). + * + * @param message the error message to print during compilation if a reference remains + * after type checking + * @since 2.11.0 + */ +@getter @setter @beanGetter @beanSetter @companionClass @companionMethod +final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 6b7aa2dddf..d70ba366c4 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -904,7 +904,7 @@ trait Definitions extends api.StandardDefinitions { lazy val BeanPropertyAttr = requiredClass[scala.beans.BeanProperty] lazy val BooleanBeanPropertyAttr = requiredClass[scala.beans.BooleanBeanProperty] - lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.reflect.internal.annotations.compileTimeOnly") + lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.annotation.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/annotations/compileTimeOnly.scala b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala deleted file mode 100644 index 2c9f909629..0000000000 --- a/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala +++ /dev/null @@ -1,32 +0,0 @@ -package scala -package reflect -package internal -package annotations - -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. - * - * Later on, this annotation should be removed and implemented with domain-specific macros. - * If a certain method `inner` mustn't be called outside the context of a given macro `outer`, - * then it should itself be declared as a macro. - * - * Approach #1. Expansion of `inner` checks whether its enclosures contain `outer` and - * report an error if `outer` is not detected. In principle, we could use this approach right now, - * but currently enclosures are broken, because contexts aren't exactly famous for keeping precise - * track of the stack of the trees being typechecked. - * - * Approach #2. Default implementation of `inner` is just an invocation of `c.abort`. - * `outer` is an untyped macro, which expands into a block, which contains a redefinition of `inner` - * and a call to itself. The redefined `inner` could either be a stub like `Expr.splice` or carry out - * domain-specific logic. - * - * @param message the error message to print during compilation if a reference remains - * after type checking - * @since 2.10.1 - */ -@getter @setter @beanGetter @beanSetter -final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/src/reflect/scala/reflect/internal/annotations/package.scala b/src/reflect/scala/reflect/internal/annotations/package.scala new file mode 100644 index 0000000000..ef299a600c --- /dev/null +++ b/src/reflect/scala/reflect/internal/annotations/package.scala @@ -0,0 +1,6 @@ +package scala.reflect.internal + +package object annotations { + @deprecated("Use scala.annotation.compileTimeOnly instead", "2.11.0") + type compileTimeOnly = scala.annotation.compileTimeOnly +} \ No newline at end of file diff --git a/test/files/neg/compile-time-only-a.check b/test/files/neg/compile-time-only-a.check new file mode 100644 index 0000000000..99d7a8e2dd --- /dev/null +++ b/test/files/neg/compile-time-only-a.check @@ -0,0 +1,19 @@ +compile-time-only-a.scala:41: error: C5 + 2.ext + ^ +compile-time-only-a.scala:42: error: C5 + C5(2) + ^ +compile-time-only-a.scala:45: error: C6.x + val _ = c6.x + ^ +compile-time-only-a.scala:46: error: C6.foo + c6.foo + ^ +compile-time-only-a.scala:48: error: C6.y + c6.y = c6.y + ^ +compile-time-only-a.scala:48: error: C6.y + c6.y = c6.y + ^ +6 errors found diff --git a/test/files/neg/compile-time-only-a.scala b/test/files/neg/compile-time-only-a.scala new file mode 100644 index 0000000000..43d36dfab1 --- /dev/null +++ b/test/files/neg/compile-time-only-a.scala @@ -0,0 +1,57 @@ +import scala.annotation.compileTimeOnly + +@compileTimeOnly("C1") class C1 +object C1 + +class C2 +@compileTimeOnly("C2") object C2 + +@compileTimeOnly("C3") case class C3(x: Int) + +@compileTimeOnly("C4") case class C4(x: Int) +object C4 + +object pkg { + @compileTimeOnly("C5") + implicit class C5(val x: Int) { + def ext = ??? + } +} + +class C6(@compileTimeOnly("C6.x") val x: Int) { + @compileTimeOnly("C6.foo") def foo = 2 + @compileTimeOnly("C6.Foo") type Foo = Int + @compileTimeOnly("C6.y") var y = 3 +} + +object Test extends App { + new C1() + C1 + + new C2() + C2 + + new C3(2) + C3(2) + + new C4(2) + C4(2) + + import pkg._ + 2.ext + C5(2) + + val c6 = new C6(2) + val _ = c6.x + c6.foo + type Foo = c6.Foo + c6.y = c6.y +} + +@compileTimeOnly("placebo") +class placebo extends scala.annotation.StaticAnnotation + +@placebo +class Test { + @placebo def x = (2: @placebo) +} \ No newline at end of file -- cgit v1.2.3 From 840ad7656456f014135c2a5e31e0d9ffd63625bd Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 10 Aug 2013 14:52:24 +0200 Subject: marks Expr.splice and Expr.value with @compileTimeOnly Now that @compileTimeOnly is part of the standard library, why don't we use it within the standard library. --- src/reflect/scala/reflect/api/Exprs.scala | 5 +++-- test/files/neg/compile-time-only-b.check | 7 +++++++ test/files/neg/compile-time-only-b.scala | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/compile-time-only-b.check create mode 100644 test/files/neg/compile-time-only-b.scala diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala index 009d9dbfdb..5b6ff2325c 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -8,6 +8,7 @@ package reflect package api import scala.reflect.runtime.{universe => ru} +import scala.annotation.compileTimeOnly /** * EXPERIMENTAL @@ -91,7 +92,7 @@ trait Exprs { self: Universe => * }}} * because expr of type Expr[T] itself does not have a method foo. */ - // @compileTimeOnly("Cannot use splice outside reify") + @compileTimeOnly("splice must be enclosed within a reify {} block") def splice: T /** @@ -108,7 +109,7 @@ trait Exprs { self: Universe => * object Impls { def foo_impl(c: Context)(x: c.Expr[X]): c.Expr[x.value.T] = ... } * }}} */ - // @compileTimeOnly("Cannot use value except for signatures of macro implementations") + @compileTimeOnly("cannot use value except for signatures of macro implementations") val value: T override def canEqual(x: Any) = x.isInstanceOf[Expr[_]] diff --git a/test/files/neg/compile-time-only-b.check b/test/files/neg/compile-time-only-b.check new file mode 100644 index 0000000000..8292a0ddeb --- /dev/null +++ b/test/files/neg/compile-time-only-b.check @@ -0,0 +1,7 @@ +compile-time-only-b.scala:13: error: splice must be enclosed within a reify {} block + val ignored3 = reify(fortyTwo).splice + ^ +compile-time-only-b.scala:14: error: cannot use value except for signatures of macro implementations + val ignored4 = reify(fortyTwo).value + ^ +two errors found diff --git a/test/files/neg/compile-time-only-b.scala b/test/files/neg/compile-time-only-b.scala new file mode 100644 index 0000000000..d5568dbe67 --- /dev/null +++ b/test/files/neg/compile-time-only-b.scala @@ -0,0 +1,15 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + // HAHA!!! + // no compileTimeOnly errors here, because scalac does constant folding + // the type of reify(42) is Expr[42.type] + // therefore the type of expr.splice is 42.type, which is then constfolded + val expr = reify(42) + val ignored1 = expr.splice + val ignored2 = expr.value + + val fortyTwo = 42 + val ignored3 = reify(fortyTwo).splice + val ignored4 = reify(fortyTwo).value +} \ No newline at end of file -- cgit v1.2.3 From 4ca45cb4c14595d491cc0cfc509fa88fb4dd5619 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 10 Aug 2013 14:58:09 +0200 Subject: @compileTimeOnly now works for annotations http://docs.scala-lang.org/overviews/macros/annotations.html say sincere "thank you!". --- src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 1 + test/files/neg/compile-time-only-a.check | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 1b6963b598..782a2880c2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1414,6 +1414,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def applyRefchecksToAnnotations(tree: Tree): Unit = { def applyChecks(annots: List[AnnotationInfo]) = { + annots foreach (annot => checkCompileTimeOnly(annot.atp.typeSymbol, annot.pos)) checkAnnotations(annots map (_.atp), tree) transformTrees(annots flatMap (_.args)) } diff --git a/test/files/neg/compile-time-only-a.check b/test/files/neg/compile-time-only-a.check index 99d7a8e2dd..3a6ded7cfc 100644 --- a/test/files/neg/compile-time-only-a.check +++ b/test/files/neg/compile-time-only-a.check @@ -16,4 +16,13 @@ compile-time-only-a.scala:48: error: C6.y compile-time-only-a.scala:48: error: C6.y c6.y = c6.y ^ -6 errors found +compile-time-only-a.scala:54: error: placebo +@placebo + ^ +compile-time-only-a.scala:56: error: placebo + @placebo def x = (2: @placebo) + ^ +compile-time-only-a.scala:56: error: placebo + @placebo def x = (2: @placebo) + ^ +9 errors found -- cgit v1.2.3 From 46f17f114bf261dc8757b5b876da50d53bb31172 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 10 Aug 2013 15:02:50 +0200 Subject: @compileTimeOnly now works for symbols from the empty package Looks like we've got the entire language covered now. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 1 + test/files/neg/compile-time-only-a.check | 23 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 782a2880c2..04d03d47cc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1649,6 +1649,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans tree case Ident(name) => + checkCompileTimeOnly(tree.symbol, tree.pos) transformCaseApply(tree, if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) { assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug diff --git a/test/files/neg/compile-time-only-a.check b/test/files/neg/compile-time-only-a.check index 3a6ded7cfc..1c4c72171f 100644 --- a/test/files/neg/compile-time-only-a.check +++ b/test/files/neg/compile-time-only-a.check @@ -1,3 +1,24 @@ +compile-time-only-a.scala:9: error: C3 +@compileTimeOnly("C3") case class C3(x: Int) + ^ +compile-time-only-a.scala:11: error: C4 +@compileTimeOnly("C4") case class C4(x: Int) + ^ +compile-time-only-a.scala:16: error: C5 + implicit class C5(val x: Int) { + ^ +compile-time-only-a.scala:28: error: C1 + new C1() + ^ +compile-time-only-a.scala:32: error: C2 + C2 + ^ +compile-time-only-a.scala:34: error: C3 + new C3(2) + ^ +compile-time-only-a.scala:37: error: C4 + new C4(2) + ^ compile-time-only-a.scala:41: error: C5 2.ext ^ @@ -25,4 +46,4 @@ compile-time-only-a.scala:56: error: placebo compile-time-only-a.scala:56: error: placebo @placebo def x = (2: @placebo) ^ -9 errors found +16 errors found -- cgit v1.2.3