diff options
-rw-r--r-- | bincompat-backward.whitelist.conf | 4 | ||||
-rw-r--r-- | bincompat-forward.whitelist.conf | 16 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 51 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Definitions.scala | 1 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 6 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/annotations/uncheckedBounds.scala | 13 | ||||
-rw-r--r-- | test/files/neg/t7694b.check | 7 | ||||
-rw-r--r-- | test/files/pos/t7694.scala | 40 |
9 files changed, 134 insertions, 13 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 31262e047e..3012ce4eeb 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -247,6 +247,10 @@ filter { { matchName="scala.reflect.runtime.SymbolLoaders.isInvalidClassName" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Types.uncheckedBounds" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index c6b7b38e8d..c92538b35a 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -531,6 +531,22 @@ filter { { matchName="scala.reflect.runtime.SymbolLoaders.isInvalidClassName" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.uncheckedBounds" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Types.uncheckedBounds" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Definitions#DefinitionsClass.UncheckedBoundsClass" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.annotations.uncheckedBounds" + problemName=MissingClassProblem } ] } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index f3736f1519..bd2cac81ea 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -164,7 +164,7 @@ trait NamesDefaults { self: Analyzer => // never used for constructor calls, they always have a stable qualifier def blockWithQualifier(qual: Tree, selected: Name) = { - val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos) setInfo qual.tpe + val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos) setInfo uncheckedBounds(qual.tpe) blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 @@ -289,9 +289,10 @@ trait NamesDefaults { self: Analyzer => // We have to deconst or types inferred from literal arguments will be Constant(_), e.g. pos/z1730.scala. gen.stableTypeFor(arg).filter(_ <:< paramTpe).getOrElse(arg.tpe).deconst ) - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( - if (byName) functionType(Nil, argTpe) else argTpe - ) + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo { + val tp = if (byName) functionType(Nil, argTpe) else argTpe + uncheckedBounds(tp) + } Some((context.scope.enter(s), byName, repeated)) }) map2(symPs, args) { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 9c374e85ea..db899b44f9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1513,17 +1513,35 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans false } - private def checkTypeRef(tp: Type, tree: Tree) = tp match { + private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean) = tp match { case TypeRef(pre, sym, args) => checkDeprecated(sym, tree.pos) if(sym.isJavaDefined) sym.typeParams foreach (_.cookJavaRawInfo()) - if (!tp.isHigherKinded) + if (!tp.isHigherKinded && !skipBounds) checkBounds(tree, pre, sym.owner, sym.typeParams, args) case _ => } - private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach (tp => checkTypeRef(tp, tree)) + private def checkTypeRefBounds(tp: Type, tree: Tree) = { + var skipBounds = false + tp match { + case AnnotatedType(ann :: Nil, underlying, selfSym) if ann.symbol == UncheckedBoundsClass => + skipBounds = true + underlying + case TypeRef(pre, sym, args) => + if (!tp.isHigherKinded && !skipBounds) + checkBounds(tree, pre, sym.owner, sym.typeParams, args) + tp + case _ => + tp + } + } + + private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp => + checkTypeRef(tp, tree, skipBounds = false) + checkTypeRefBounds(tp, tree) + } private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f private def applyRefchecksToAnnotations(tree: Tree): Unit = { @@ -1551,8 +1569,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } doTypeTraversal(tree) { - case AnnotatedType(annots, _, _) => applyChecks(annots) - case _ => + case tp @ AnnotatedType(annots, _, _) => + applyChecks(annots) + case tp => } case _ => } @@ -1735,13 +1754,27 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } val existentialParams = new ListBuffer[Symbol] - doTypeTraversal(tree) { // check all bounds, except those that are existential type parameters - case ExistentialType(tparams, tpe) => + var skipBounds = false + // check all bounds, except those that are existential type parameters + // or those within typed annotated with @uncheckedBounds + doTypeTraversal(tree) { + case tp @ ExistentialType(tparams, tpe) => existentialParams ++= tparams - case t: TypeRef => - checkTypeRef(deriveTypeWithWildcards(existentialParams.toList)(t), tree) + case ann: AnnotatedType if ann.hasAnnotation(UncheckedBoundsClass) => + // SI-7694 Allow code synthetizers 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.tpe = tree.tpe.map { + _.filterAnnotations(_.symbol != UncheckedBoundsClass) + } + } + tree case TypeApply(fn, args) => diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 629d82d254..09d7af82d1 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -983,6 +983,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ThrowsClass = requiredClass[scala.throws[_]] lazy val TransientAttr = requiredClass[scala.transient] lazy val UncheckedClass = requiredClass[scala.unchecked] + lazy val UncheckedBoundsClass = getClassIfDefined("scala.reflect.internal.annotations.uncheckedBounds") lazy val UnspecializedClass = requiredClass[scala.annotation.unspecialized] lazy val VolatileAttr = requiredClass[scala.volatile] diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fc3f5de77f..563578344d 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -7314,6 +7314,12 @@ trait Types extends api.Types { self: SymbolTable => else (ps :+ SerializableClass.tpe).toList ) + /** Adds the @uncheckedBound annotation if the given `tp` has type arguments */ + final def uncheckedBounds(tp: Type): Type = { + if (tp.typeArgs.isEmpty || UncheckedBoundsClass == NoSymbol) tp // second condition for backwards compatibilty with older scala-reflect.jar + else tp.withAnnotation(AnnotationInfo marker UncheckedBoundsClass.tpe) + } + /** Members of the given class, other than those inherited * from Any or AnyRef. */ diff --git a/src/reflect/scala/reflect/internal/annotations/uncheckedBounds.scala b/src/reflect/scala/reflect/internal/annotations/uncheckedBounds.scala new file mode 100644 index 0000000000..a44bb54734 --- /dev/null +++ b/src/reflect/scala/reflect/internal/annotations/uncheckedBounds.scala @@ -0,0 +1,13 @@ +package scala.reflect +package internal +package annotations + +/** + * An annotation that designates the annotated type should not be checked for violations of + * type parameter bounds in the `refchecks` phase of the compiler. This can be used by synthesized + * code the uses an inferred type of an expression as the type of an artifict val/def (for example, + * a temporary value introduced by an ANF transform). See [[https://issues.scala-lang.org/browse/SI-7694]]. + * + * @since 2.10.3 + */ +final class uncheckedBounds extends scala.annotation.StaticAnnotation diff --git a/test/files/neg/t7694b.check b/test/files/neg/t7694b.check new file mode 100644 index 0000000000..ea3d7736f8 --- /dev/null +++ b/test/files/neg/t7694b.check @@ -0,0 +1,7 @@ +t7694b.scala:8: error: type arguments [_3,_4] do not conform to trait L's type parameter bounds [A2,B2 <: A2] + def d = if (true) (null: L[A, A]) else (null: L[B, B]) + ^ +t7694b.scala:9: error: type arguments [_1,_2] do not conform to trait L's type parameter bounds [A2,B2 <: A2] + val v = if (true) (null: L[A, A]) else (null: L[B, B]) + ^ +two errors found diff --git a/test/files/pos/t7694.scala b/test/files/pos/t7694.scala new file mode 100644 index 0000000000..9852d5ec79 --- /dev/null +++ b/test/files/pos/t7694.scala @@ -0,0 +1,40 @@ +trait A +trait B + +trait L[A2, B2 <: A2] { + def bar(a: Any, b: Any) = 0 +} + +object Lub { + // use named args transforms to include TypeTree(<lub.tpe>) in the AST before refchecks. + def foo(a: L[_, _], b: Any) = 0 + + foo(b = 0, a = if (true) (null: L[A, A]) else (null: L[B, B])) + + (if (true) (null: L[A, A]) else (null: L[B, B])).bar(b = 0, a = 0) +} + +/* +The LUB ends up as: + +TypeRef( + TypeSymbol( + abstract trait L#7038[A2#7039, B2#7040 <: A2#7039] extends AnyRef#2197 + + ) + args = List( + AbstractTypeRef( + AbstractType( + type _1#13680 >: A#7036 with B#7037 <: Object#1752 + ) + ) + AbstractTypeRef( + AbstractType( + type _2#13681 >: A#7036 with B#7037 <: Object#1752 + ) + ) + ) +) + +Note that type _2#13681 is *not* bound by _1#13680 +*/ |