Skip to content

Commit

Permalink
RefCheck types uniformly - handle existentials and annotations
Browse files Browse the repository at this point in the history
 - All existentially bound skolems are replaced with wildcards
 - Annotation types are checked deeply
 - Nesting of `@uncheckedBounds` is handled properly
  • Loading branch information
joroKr21 committed Nov 8, 2021
1 parent af79b08 commit eace079
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 48 deletions.
94 changes: 46 additions & 48 deletions src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
Expand Up @@ -1407,32 +1407,53 @@ abstract class RefChecks extends Transform {
false
}

private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean): Unit = tp match {
case TypeRef(pre, sym, args) =>
tree match {
case tt: TypeTree if tt.original == null => // scala/bug#7783 don't warn about inferred types
// FIXME: reconcile this check with one in resetAttrs
case _ => checkUndesiredProperties(sym, tree.pos)
private object RefCheckTypeMap extends TypeMap {
object ExistentialToWildcard extends TypeMap {
override def apply(tpe: Type): Type =
if (tpe.typeSymbol.isExistential) WildcardType else tpe.mapOver(this)
}

private[this] var skipBounds = false
private[this] var tree: Tree = EmptyTree

def check(tpe: Type, tree: Tree): Type = {
this.tree = tree
try apply(tpe) finally {
skipBounds = false
this.tree = EmptyTree
}
if (sym.isJavaDefined)
sym.typeParams foreach (_.cookJavaRawInfo())
if (!tp.isHigherKinded && !skipBounds)
checkBounds(tree, pre, sym.owner, sym.typeParams, args)
case _ =>
}
}

private def checkTypeRefBounds(tp: Type, tree: Tree) = {
var skipBounds = false
tp match {
case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass =>
// check all bounds, except those that are existential type parameters
// or those within typed annotated with @uncheckedBounds
override def apply(tpe: Type): Type = tpe match {
case tpe: AnnotatedType if tpe.hasAnnotation(UncheckedBoundsClass) =>
// scala/bug#7694 Allow code synthesizers to disable checking of bounds for TypeTrees based on inferred LUBs
// which might not conform to the constraints.
val savedSkipBounds = skipBounds
skipBounds = true
underlying
try tpe.mapOver(this).filterAnnotations(_.symbol != UncheckedBoundsClass)
finally skipBounds = savedSkipBounds
case tpe: TypeRef =>
checkTypeRef(ExistentialToWildcard(tpe))
tpe.mapOver(this)
case tpe =>
tpe.mapOver(this)
}

private def checkTypeRef(tpe: Type): Unit = tpe match {
case TypeRef(pre, sym, args) =>
if (!tp.isHigherKinded && !skipBounds)
tree match {
// scala/bug#7783 don't warn about inferred types
// FIXME: reconcile this check with one in resetAttrs
case tree: TypeTree if tree.original == null =>
case tree => checkUndesiredProperties(sym, tree.pos)
}
if (sym.isJavaDefined)
sym.typeParams.foreach(_.cookJavaRawInfo())
if (!tpe.isHigherKinded && !skipBounds)
checkBounds(tree, pre, sym.owner, sym.typeParams, args)
tp
case _ =>
tp
}
}

Expand All @@ -1449,8 +1470,7 @@ abstract class RefChecks extends Transform {
def applyChecks(annots: List[AnnotationInfo]): List[AnnotationInfo] = if (annots.isEmpty) Nil else {
annots.foreach { ann =>
checkVarArgs(ann.atp, tree)
checkTypeRef(ann.atp, tree, skipBounds = false)
checkTypeRefBounds(ann.atp, tree)
RefCheckTypeMap.check(ann.atp, tree)
if (ann.original != null && ann.original.hasExistingSymbol)
checkUndesiredProperties(ann.original.symbol, tree.pos)
}
Expand Down Expand Up @@ -1755,29 +1775,8 @@ abstract class RefChecks extends Transform {
}
}

val existentialParams = new ListBuffer[Symbol]
var skipBounds = false
// check all bounds, except those that are existential type parameters
// or those within typed annotated with @uncheckedBounds
if (!inPattern) tree.tpe foreach {
case tp @ ExistentialType(tparams, tpe) =>
existentialParams ++= tparams
case ann: AnnotatedType if ann.hasAnnotation(UncheckedBoundsClass) =>
// scala/bug#7694 Allow code synthesizers to disable checking of bounds for TypeTrees based on inferred LUBs
// which might not conform to the constraints.
skipBounds = true
case tp: TypeRef =>
val tpWithWildcards = deriveTypeWithWildcards(existentialParams.toList)(tp)
checkTypeRef(tpWithWildcards, tree, skipBounds)
case _ =>
}
if (skipBounds) {
tree.setType(tree.tpe.map {
_.filterAnnotations(_.symbol != UncheckedBoundsClass)
})
}

tree
if (inPattern) tree
else tree.setType(RefCheckTypeMap.check(tree.tpe, tree))

case TypeApply(fn, args) =>
checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe))
Expand Down Expand Up @@ -1812,9 +1811,8 @@ abstract class RefChecks extends Transform {
case x @ Select(_, _) =>
transformSelect(x)

case Literal(Constant(tp: Type)) =>
checkTypeRef(tp, tree, skipBounds = false)
tree
case Literal(Constant(tpe: Type)) =>
tree.setType(RefCheckTypeMap.check(tpe, tree))

case UnApply(fun, args) =>
transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun)
Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/ref-checks.check
@@ -0,0 +1,7 @@
ref-checks.scala:8: error: type arguments [Int] do not conform to trait Chars's type parameter bounds [A <: CharSequence]
@ann[Chars[Int]] val x = 42
^
ref-checks.scala:9: error: type arguments [Double] do not conform to trait Chars's type parameter bounds [A <: CharSequence]
val y: Two[Chars[Long] @uncheckedBounds, Chars[Double]] = null
^
2 errors
10 changes: 10 additions & 0 deletions test/files/neg/ref-checks.scala
@@ -0,0 +1,10 @@
import scala.annotation.StaticAnnotation
import scala.reflect.internal.annotations.uncheckedBounds

object Test {
trait Chars[A <: CharSequence]
trait Two[A, B]
class ann[A] extends StaticAnnotation
@ann[Chars[Int]] val x = 42
val y: Two[Chars[Long] @uncheckedBounds, Chars[Double]] = null
}
2 changes: 2 additions & 0 deletions test/files/run/t12481.check
@@ -0,0 +1,2 @@
Test$Universe[_ <: Any]
Test$Universe[<?>]
6 changes: 6 additions & 0 deletions test/files/run/t12481.scala
@@ -0,0 +1,6 @@
object Test extends App {
trait Txn[T <: Txn[T]]
trait Universe[T <: Txn[T]]
println(implicitly[Manifest[Universe[_]]])
println(implicitly[OptManifest[Universe[_]]])
}

0 comments on commit eace079

Please sign in to comment.