summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-01-14 23:29:50 -0800
committerPaul Phillips <paulp@improving.org>2013-01-26 11:19:36 -0800
commit832fc9a67e5aa85bdde61883527d3ac9554094d7 (patch)
treeb5bc08f2ef57331efa66c956112bface14cd530f
parent72f36cbc80d5667c5ada3b7c0fe60435a4b202ad (diff)
downloadscala-832fc9a67e5aa85bdde61883527d3ac9554094d7.tar.gz
scala-832fc9a67e5aa85bdde61883527d3ac9554094d7.tar.bz2
scala-832fc9a67e5aa85bdde61883527d3ac9554094d7.zip
SI-2577, SI-6860: annotation type inference.
This is less than ideal: scala> class Bippy[T] extends annotation.StaticAnnotation defined class Bippy scala> def f: Int @Bippy = 5 f: Int @Bippy[T] Turns out we can infer such types. Now it says: scala> def f: Int @Bippy = 5 f: Int @Bippy[Nothing] This should put to rest many an issue with parameterized annotations.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala37
-rw-r--r--src/library/scala/throws.scala2
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala11
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala6
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala11
-rw-r--r--test/files/pos/annotations2.scala31
-rw-r--r--test/files/run/t2577.check1
-rw-r--r--test/files/run/t2577.scala17
-rw-r--r--test/files/run/t6860.check4
-rw-r--r--test/files/run/t6860.scala20
10 files changed, 113 insertions, 27 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index f2f8f47bf2..c12233b726 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3458,31 +3458,28 @@ trait Typers extends Adaptations with Tags {
}
// begin typedAnnotation
- val (fun, argss) = {
- def extract(fun: Tree, outerArgss: List[List[Tree]]):
- (Tree, List[List[Tree]]) = fun match {
- case Apply(f, args) =>
- extract(f, args :: outerArgss)
- case Select(New(tpt), nme.CONSTRUCTOR) =>
- (fun, outerArgss)
- case _ =>
- reportAnnotationError(UnexpectedTreeAnnotation(fun))
- (setError(fun), outerArgss)
- }
- extract(ann, List())
- }
+ val treeInfo.Applied(fun0, targs, argss) = treeInfo.dissectApplied(ann)
+ val typedFun0 = typed(fun0, forFunMode(mode), WildcardType)
+ val typedFunPart = (
+ // If there are dummy type arguments in typeFun part, it suggests we
+ // must type the actual constructor call, not only the select. The value
+ // arguments are how the type arguments will be inferred.
+ if (targs.isEmpty && typedFun0.exists(t => isDummyAppliedType(t.tpe)))
+ logResult(s"Retyped $typedFun0 to find type args")(typed(argss.foldLeft(fun0)(Apply(_, _))))
+ else
+ typedFun0
+ )
+ val typedFun @ Select(New(annTpt), _) = treeInfo.dissectApplied(typedFunPart).core
+ val annType = annTpt.tpe
- val res = if (fun.isErroneous) ErroneousAnnotation
+ val res = if (typedFun.isErroneous) ErroneousAnnotation
else {
- val typedFun @ Select(New(tpt), _) = typed(fun, mode.forFunMode, WildcardType)
- val annType = tpt.tpe
-
if (typedFun.isErroneous) ErroneousAnnotation
else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) {
// annotation to be saved as java classfile annotation
val isJava = typedFun.symbol.owner.isJavaDefined
if (!annType.typeSymbol.isNonBottomSubClass(annClass)) {
- reportAnnotationError(AnnotationTypeMismatchError(tpt, annClass.tpe, annType))
+ reportAnnotationError(AnnotationTypeMismatchError(annTpt, annType, annType))
} else if (argss.length > 1) {
reportAnnotationError(MultipleArgumentListForAnnotationError(ann))
} else {
@@ -3534,7 +3531,7 @@ trait Typers extends Adaptations with Tags {
val typedAnn = if (selfsym == NoSymbol) {
// local dummy fixes SI-5544
val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos)))
- localTyper.typed(ann, mode, annClass.tpe)
+ localTyper.typed(ann, mode, annType)
}
else {
// Since a selfsym is supplied, the annotation should have an extra
@@ -3548,7 +3545,7 @@ trait Typers extends Adaptations with Tags {
// sometimes does. The problem is that "self" ident's within
// annot.constr will retain the old symbol from the previous typing.
val func = Function(funcparm :: Nil, ann.duplicate)
- val funcType = appliedType(FunctionClass(1), selfsym.info, annClass.tpe_*)
+ val funcType = appliedType(FunctionClass(1), selfsym.info, annType)
val Function(arg :: Nil, rhs) = typed(func, mode, funcType)
rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil)
diff --git a/src/library/scala/throws.scala b/src/library/scala/throws.scala
index 159f1f02f4..5a5dd9a1f5 100644
--- a/src/library/scala/throws.scala
+++ b/src/library/scala/throws.scala
@@ -24,5 +24,5 @@ package scala
* @since 2.1
*/
class throws[T <: Throwable](cause: String = "") extends scala.annotation.StaticAnnotation {
- def this(clazz: Class[T]) = this()
+ def this(clazz: Class[T]) = this("")
}
diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
index 7a972c3f1a..70b8bd9be5 100644
--- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala
+++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
@@ -12,7 +12,7 @@ import scala.collection.immutable.ListMap
/** AnnotationInfo and its helpers */
trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
- import definitions.{ ThrowsClass, StaticAnnotationClass, isMetaAnnotation }
+ import definitions.{ ThrowsClass, ThrowableClass, StaticAnnotationClass, isMetaAnnotation }
// Common annotation code between Symbol and Type.
// For methods altering the annotation list, on Symbol it mutates
@@ -334,7 +334,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
* as well as “new-stye” `@throws[Exception]("cause")` annotations.
*/
object ThrownException {
- def unapply(ann: AnnotationInfo): Option[Symbol] =
+ def unapply(ann: AnnotationInfo): Option[Symbol] = {
ann match {
case AnnotationInfo(tpe, _, _) if tpe.typeSymbol != ThrowsClass =>
None
@@ -342,8 +342,11 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
case AnnotationInfo(_, List(Literal(Constant(tpe: Type))), _) =>
Some(tpe.typeSymbol)
// new-style: @throws[Exception], @throws[Exception]("cause")
- case AnnotationInfo(TypeRef(_, _, args), _, _) =>
- Some(args.head.typeSymbol)
+ case AnnotationInfo(TypeRef(_, _, arg :: _), _, _) =>
+ Some(arg.typeSymbol)
+ case AnnotationInfo(TypeRef(_, _, Nil), _, _) =>
+ Some(ThrowableClass)
}
+ }
}
}
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index 032a4aebef..c90e94c1c1 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -605,6 +605,12 @@ abstract class TreeInfo {
}
loop(tree)
}
+
+ override def toString = {
+ val tstr = if (targs.isEmpty) "" else targs.mkString("[", ", ", "]")
+ val astr = argss map (args => args.mkString("(", ", ", ")")) mkString ""
+ s"$core$tstr$astr"
+ }
}
/** Returns a wrapper that knows how to destructure and analyze applications.
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index b336192b67..b51028c502 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -2944,12 +2944,19 @@ trait Types extends api.Types { self: SymbolTable =>
else existentialAbstraction(existentialsInType(tp), tp)
)
- def containsExistential(tpe: Type) =
- tpe exists typeIsExistentiallyBound
+ def containsDummyTypeArg(tp: Type) = tp exists isDummyTypeArg
+ def isDummyTypeArg(tp: Type) = tp.typeSymbol.isTypeParameter
+ def isDummyAppliedType(tp: Type) = tp match {
+ case TypeRef(_, _, args) if args.nonEmpty => args exists isDummyTypeArg
+ case _ => false
+ }
def existentialsInType(tpe: Type) =
tpe withFilter typeIsExistentiallyBound map (_.typeSymbol)
+ def containsExistential(tpe: Type) =
+ tpe exists typeIsExistentiallyBound
+
/** Precondition: params.nonEmpty. (args.nonEmpty enforced structurally.)
*/
class HKTypeVar(
diff --git a/test/files/pos/annotations2.scala b/test/files/pos/annotations2.scala
new file mode 100644
index 0000000000..3bce7f8ac4
--- /dev/null
+++ b/test/files/pos/annotations2.scala
@@ -0,0 +1,31 @@
+
+class B[T](x: (T, T)) {
+ def this(xx: (T, Any, Any)) = this((xx._1, xx._1))
+}
+class BAnn[T](x: (T, T)) extends scala.annotation.StaticAnnotation {
+ def this(xx: (T, Any, Any)) = this((xx._1, xx._1))
+}
+class CAnn[T](x: (T, T)) extends scala.annotation.StaticAnnotation {
+ def this(xx: Class[T]) = this((xx.newInstance(), xx.newInstance()))
+}
+
+class A1 {
+ val b1 = new B((1, 2, 3))
+ val b2 = new B((1, 2))
+ val b3 = new B[Int]((1, 2, 3))
+ val b4 = new B[Int]((1, 2))
+}
+
+class A2 {
+ @BAnn((1, 2, 3)) val b1 = null
+ @BAnn((1, 2)) val b2 = null
+ @BAnn[Int]((1, 2, 3)) val b3 = null
+ @BAnn[Int]((1, 2)) val b4 = null
+}
+
+class A3 {
+ @CAnn(classOf[Int]) val b1 = null
+ @CAnn((1, 2)) val b2 = null
+ @CAnn[Int](classOf[Int]) val b3 = null
+ @CAnn[Int]((1, 2)) val b4 = null
+}
diff --git a/test/files/run/t2577.check b/test/files/run/t2577.check
new file mode 100644
index 0000000000..4a584e4989
--- /dev/null
+++ b/test/files/run/t2577.check
@@ -0,0 +1 @@
+Nothing
diff --git a/test/files/run/t2577.scala b/test/files/run/t2577.scala
new file mode 100644
index 0000000000..6d836a3996
--- /dev/null
+++ b/test/files/run/t2577.scala
@@ -0,0 +1,17 @@
+case class annot[T]() extends scala.annotation.StaticAnnotation
+
+// type inference should infer @annot[Nothing] instead of @annot[T]
+// note the T is not in scope here!
+class Foo[@annot U]
+
+object Test {
+ import scala.reflect.runtime.universe._
+ val x = new Foo
+
+ def main(args: Array[String]): Unit = {
+ val targ = typeOf[x.type].widen match {
+ case TypeRef(_, _, arg :: _) => arg
+ }
+ println(targ)
+ }
+}
diff --git a/test/files/run/t6860.check b/test/files/run/t6860.check
new file mode 100644
index 0000000000..c96331f540
--- /dev/null
+++ b/test/files/run/t6860.check
@@ -0,0 +1,4 @@
+Bippy[String]
+Bippy[String]
+throws[Nothing]
+throws[RuntimeException]
diff --git a/test/files/run/t6860.scala b/test/files/run/t6860.scala
new file mode 100644
index 0000000000..2dcc2a67f7
--- /dev/null
+++ b/test/files/run/t6860.scala
@@ -0,0 +1,20 @@
+class Bippy[T](val value: T) extends annotation.StaticAnnotation
+
+class A {
+ @Bippy("hi") def f1: Int = 1
+ @Bippy[String]("hi") def f2: Int = 2
+
+ @throws("what do I throw?") def f3 = throw new RuntimeException
+ @throws[RuntimeException]("that's good to know!") def f4 = throw new RuntimeException
+}
+
+object Test {
+ import scala.reflect.runtime.universe._
+
+ def main(args: Array[String]): Unit = {
+ val members = typeOf[A].declarations.toList
+ val tpes = members flatMap (_.annotations) map (_.tpe)
+
+ tpes.map(_.toString).sorted foreach println
+ }
+}