Skip to content

Commit

Permalink
Experiment: restrict allowed trees in type annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
mbovel committed Oct 23, 2024
1 parent aa9115d commit a1d0910
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 51 deletions.
47 changes: 47 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package dotc
package core

import Symbols.*, Types.*, Contexts.*, Constants.*, Phases.*
import Decorators.i
import StdNames.nme
import ast.tpd, tpd.*
import util.Spans.Span
import printing.{Showable, Printer}
Expand Down Expand Up @@ -106,6 +108,51 @@ object Annotations {
go(metaSyms) || orNoneOf.nonEmpty && !go(orNoneOf)
}

/** Checks if this annotation can be used as a type annotation. Reports one
* or more errors if it cannot.
*/
final def checkValidTypeAnnotation()(using Context): Unit =
def isTupleModule(sym: Symbol): Boolean =
ctx.definitions.isTupleClass(sym.companionClass)

def isFunctionAllowed(t: Tree): Boolean =
t match
case Select(qual, nme.apply) => qual.symbol == defn.ArrayModule || isTupleModule(qual.symbol)
case TypeApply(fun, _) => isFunctionAllowed(fun)
case _ => false

def check(t: Tree): Boolean =
t match
case Literal(_) => true
case Apply(fun, args) => isFunctionAllowed(fun) && args.forall(check)
case SeqLiteral(elems, _) => elems.forall(check)
case Typed(expr, _) => check(expr)
case NamedArg(_, arg) => check(arg)
case _ =>
t.tpe.stripped match
case _: SingletonType => true
// We need to handle type refs for these test cases:
// - tests/pos/dependent-annot.scala
// - tests/pos/i16208.scala
// - tests/run/java-ann-super-class
// - tests/run/java-ann-super-class-separate
// - tests/neg/i19470.scala (@retains)
// Why do we get type refs in these cases?
case _: TypeRef => true
case _: TypeParamRef => true
case tp => false

val uncheckedAnnots = Set[Symbol](defn.RetainsAnnot, defn.RetainsByNameAnnot)
if uncheckedAnnots(symbol) then return

for arg <- arguments if !check(arg) do
report.error(
s"""Implementation restriction: not a valid type annotation argument.
| Argument: $arg
| Type: ${arg.tpe}""".stripMargin, arg.srcPos)

()

/** Operations for hash-consing, can be overridden */
def hash: Int = System.identityHashCode(this)
def eql(that: Annotation) = this eq that
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5754,6 +5754,7 @@ object Types extends TypeUtils {
def make(underlying: Type, annots: List[Annotation])(using Context): Type =
annots.foldLeft(underlying)(apply(_, _))
def apply(parent: Type, annot: Annotation)(using Context): AnnotatedType =
annot.checkValidTypeAnnotation()
unique(CachedAnnotatedType(parent, annot))
end AnnotatedType

Expand Down
16 changes: 16 additions & 0 deletions tests/neg/annot-forbidden-type-annotations.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class annot[T](arg: T) extends scala.annotation.Annotation

def m1(x: Any): Unit = ()
val x: Int = ???

def m2(): String @annot(m1(2)) = ??? // error
def m3(): String @annot(throw new Error()) = ??? // error
def m4(): String @annot((x: Int) => x) = ??? // error
def m5(): String @annot(x + 1) = ??? // error

def main =
@annot(m1(2)) val x1: String = ??? // ok
@annot(throw new Error()) val x2: String = ??? // ok
@annot((x: Int) => x) val x3: String = ??? // ok
@annot(x + 1) val x4: String = ??? // ok
()
10 changes: 0 additions & 10 deletions tests/pos/annot-17939b.scala

This file was deleted.

1 change: 0 additions & 1 deletion tests/pos/annotDepMethType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ case class pc(calls: Any*) extends annotation.TypeConstraint
object Main {
class C0 { def baz: String = "" }
class C1 { def bar(c0: C0): String @pc(c0.baz) = c0.baz }
def trans(c1: C1): String @pc(c1.bar(throw new Error())) = c1.bar(new C0)
}
33 changes: 0 additions & 33 deletions tests/printing/annot-19846b.check

This file was deleted.

7 changes: 0 additions & 7 deletions tests/printing/annot-19846b.scala

This file was deleted.

0 comments on commit a1d0910

Please sign in to comment.