diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 8d524d8f5d00..023f7b36a173 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -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 } } @@ -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) } @@ -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)) @@ -1812,8 +1811,8 @@ abstract class RefChecks extends Transform { case x @ Select(_, _) => transformSelect(x) - case Literal(Constant(tp: Type)) => - checkTypeRef(tp, tree, skipBounds = false) + case Literal(Constant(tpe: Type)) => + RefCheckTypeMap.check(tpe, tree) tree case UnApply(fun, args) => diff --git a/test/files/neg/ref-checks.check b/test/files/neg/ref-checks.check new file mode 100644 index 000000000000..8ffa9ff27bf0 --- /dev/null +++ b/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 diff --git a/test/files/neg/ref-checks.scala b/test/files/neg/ref-checks.scala new file mode 100644 index 000000000000..58e736ec1b54 --- /dev/null +++ b/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 +} diff --git a/test/files/run/t12481.check b/test/files/run/t12481.check new file mode 100644 index 000000000000..39d6696135de --- /dev/null +++ b/test/files/run/t12481.check @@ -0,0 +1,2 @@ +Test$Universe[_ <: Any] +Test$Universe[] diff --git a/test/files/run/t12481.scala b/test/files/run/t12481.scala new file mode 100644 index 000000000000..8407c634ef54 --- /dev/null +++ b/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[_]]]) +}