Skip to content

Commit 71a5179

Browse files
committed
New attachments: ParentNew and AnnotNew
The attachments label `New` nodes that are parents or annotations. These nodes will be treated specially when it comes to checking whether a class can be instantiated. Specifically, parents and Java-defined annotations are allowed to be abstract. Also: New convenience method: isAnnotation
1 parent c37b408 commit 71a5179

File tree

5 files changed

+70
-7
lines changed

5 files changed

+70
-7
lines changed

src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
8282
case Block(stats, expr) => methPart(expr)
8383
case mp => mp
8484
}
85+
86+
/** If tree is an instance creation expression, its New node, otherwise tree itself */
87+
def newPart(tree: Tree) = methPart(tree) match {
88+
case Select(nu: untpd.New, nme.CONSTRUCTOR) => nu
89+
case _ => tree
90+
}
8591

8692
/** If tree is a closure, it's body, otherwise tree itself */
8793
def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ object SymDenotations {
382382
/** is this symbol a trait representing a type lambda? */
383383
final def isLambdaTrait(implicit ctx: Context): Boolean =
384384
isClass && name.startsWith(tpnme.LambdaPrefix)
385+
386+
/** Is this symbol an annotation? */
387+
final def isAnnotation(implicit ctx: Context): Boolean =
388+
derivesFrom(defn.AnnotationClass)
385389

386390
/** Is this symbol a package object or its module class? */
387391
def isPackageObject(implicit ctx: Context): Boolean = {
@@ -986,6 +990,9 @@ object SymDenotations {
986990

987991
def nonMemberTermRef(implicit ctx: Context): TermRef =
988992
TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm)
993+
994+
def typeRefWithArgs(implicit ctx: Context): Type =
995+
typeRef.appliedTo(typeParams.map(_.typeRef))
989996

990997
/** The variance of this type parameter or type member as an Int, with
991998
* +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ trait Applications extends Compatibility { self: Typer =>
525525

526526
methPart(fun1).tpe match {
527527
case funRef: TermRef =>
528+
labelAnnotArgs(funRef.symbol, tree)
528529
tryEither { implicit ctx =>
529530
val app =
530531
if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
@@ -578,6 +579,33 @@ trait Applications extends Compatibility { self: Typer =>
578579
else realApply
579580
}
580581

582+
/** If `meth` is a constructor of a Java annotation used as an annotation
583+
* (as opposed to a normal `new`), label every `new` everywhere in its arguments
584+
* (including nested occurrences) as annotations.
585+
* See pos/java-interop/t294 for examples.
586+
* This labeling is safe because Java annotations only accept constants and
587+
* other annotations as arguments, so a `new` must be an allocation site for
588+
* an annotation.
589+
* The labeling is necessary because we need to avoid flagging `new`'s of Java
590+
* annotation interfaces as errors because the interface is abstract.
591+
*/
592+
private def labelAnnotArgs(meth: Symbol, app: untpd.Apply)(implicit ctx: Context): Unit = {
593+
import untpd._
594+
val isJavaAnnot = meth.isConstructor && meth.is(JavaDefined) && meth.owner.isAnnotation
595+
if (newPart(app).hasAttachment(AnnotNew) && isJavaAnnot) {
596+
val labelNewsAsAnnots = new TreeTraverser {
597+
def traverse(tree: untpd.Tree)(implicit ctx: Context): Unit = {
598+
tree match {
599+
case tree: New => tree.putAttachment(AnnotNew, ())
600+
case _ =>
601+
}
602+
traverseChildren(tree)
603+
}
604+
}
605+
app.args.foreach(labelNewsAsAnnots.traverse)
606+
}
607+
}
608+
581609
/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
582610
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
583611
throw new Error(s"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,21 @@ class Namer { typer: Typer =>
155155

156156
import untpd._
157157

158+
/** Untyped tree was already typed, the attachment value is its typed versuon */
158159
val TypedAhead = new Attachment.Key[tpd.Tree]
160+
161+
/** Tree was expanded to by desugaring; the attachment value is its expansion */
159162
val ExpandedTree = new Attachment.Key[Tree]
163+
164+
/** Tree has been assigned a symbol, which is the attachment value */
160165
val SymOfTree = new Attachment.Key[Symbol]
161166

167+
/** `New` node is part of a parent constructor of a class, and can therefore be abstract */
168+
val ParentNew = new Attachment.Key[Unit]
169+
170+
/** `New` node is an annotation, can be abstract if the annotation is Java defined */
171+
val AnnotNew = new Attachment.Key[Unit]
172+
162173
/** A partial map from unexpanded member and pattern defs and to their expansions.
163174
* Populated during enterSyms, emptied during typer.
164175
*/
@@ -528,7 +539,8 @@ class Namer { typer: Typer =>
528539
case TypeApply(core, targs) => (core, targs)
529540
case core => (core, Nil)
530541
}
531-
val Select(New(tpt), nme.CONSTRUCTOR) = core
542+
val Select(nu @ New(tpt), nme.CONSTRUCTOR) = core
543+
nu.putAttachment(ParentNew, ())
532544
val targs1 = targs map (typedAheadType(_))
533545
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
534546
if (ptype.typeParams.isEmpty) ptype

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import EtaExpansion.etaExpand
2525
import dotty.tools.dotc.transform.Erasure.Boxing
2626
import util.Positions._
2727
import util.common._
28-
import util.SourcePosition
28+
import util.{SourcePosition, Attachment}
2929
import collection.mutable
3030
import annotation.tailrec
3131
import Implicits._
@@ -868,7 +868,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
868868
}
869869

870870
def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = track("typedAnnotation") {
871-
typed(annot, defn.AnnotationClass.typeRef)
871+
labelNew(annot, AnnotNew)
872+
typedExpr(annot, defn.AnnotationClass.typeRef)
872873
}
873874

874875
def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = track("typedValDef") {
@@ -940,11 +941,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
940941
*/
941942
def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = {
942943
val firstParent :: otherParents = parents
943-
if (firstParent.isType && !(cls is Trait))
944-
typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents
945-
else parents
944+
if (firstParent.isType && !(cls is Trait)) {
945+
val constr = untpd.New(untpd.TypedSplice(firstParent), Nil)
946+
labelNew(constr, ParentNew)
947+
typed(constr) :: otherParents
948+
} else parents
946949
}
947950

951+
/** If `tree` is an instance creation expression, attach the given label to its `New` node */
952+
def labelNew(tree: untpd.Tree, label: Attachment.Key[Unit]): Unit =
953+
untpd.newPart(tree) match {
954+
case nu: untpd.New => nu.putAttachment(label, ())
955+
case _ =>
956+
}
957+
948958
/** Overridden in retyper */
949959
def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree)
950960

@@ -971,7 +981,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
971981
}
972982

973983
def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") {
974-
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef)
984+
val annot1 = typedAnnotation(tree.annot)
975985
val arg1 = typed(tree.arg, pt)
976986
if (ctx.mode is Mode.Type)
977987
assignType(cpy.Annotated(tree)(annot1, arg1), annot1, arg1)

0 commit comments

Comments
 (0)