From 832fc9a67e5aa85bdde61883527d3ac9554094d7 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 14 Jan 2013 23:29:50 -0800 Subject: 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. --- .../scala/tools/nsc/typechecker/Typers.scala | 37 ++++++++++------------ src/library/scala/throws.scala | 2 +- .../scala/reflect/internal/AnnotationInfos.scala | 11 ++++--- src/reflect/scala/reflect/internal/TreeInfo.scala | 6 ++++ src/reflect/scala/reflect/internal/Types.scala | 11 +++++-- test/files/pos/annotations2.scala | 31 ++++++++++++++++++ test/files/run/t2577.check | 1 + test/files/run/t2577.scala | 17 ++++++++++ test/files/run/t6860.check | 4 +++ test/files/run/t6860.scala | 20 ++++++++++++ 10 files changed, 113 insertions(+), 27 deletions(-) create mode 100644 test/files/pos/annotations2.scala create mode 100644 test/files/run/t2577.check create mode 100644 test/files/run/t2577.scala create mode 100644 test/files/run/t6860.check create mode 100644 test/files/run/t6860.scala 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 + } +} -- cgit v1.2.3