Skip to content

Commit

Permalink
Respect uncheckedVariance in higher-kinded types
Browse files Browse the repository at this point in the history
A modification of `varianceInType` was necessary to propagate a
boolean flag that controls the behaviour wrt `@uncheckedVaraince`.
  • Loading branch information
joroKr21 committed May 4, 2020
1 parent 8bd496c commit bacd612
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 5 deletions.
15 changes: 10 additions & 5 deletions src/reflect/scala/reflect/internal/Variances.scala
Expand Up @@ -182,7 +182,7 @@ trait Variances {

private def checkPolyTypeParam(pt: PolyType, tparam: Symbol, tpe: Type): Unit =
if (!tparam.isInvariant) {
val required = varianceInType(tpe)(tparam)
val required = varianceInType(tpe, considerUnchecked = true)(tparam)
if (!required.isBivariant && tparam.variance != required)
issueVarianceError(ownerOf(pt), tparam, required, pt)
}
Expand Down Expand Up @@ -266,20 +266,22 @@ trait Variances {
Variance.foldExtract(tps)(t => varianceInType(t)(tparam))

/** Compute variance of type parameter `tparam` in type `tp`. */
final def varianceInType(tp: Type)(tparam: Symbol): Variance = {
varianceInTypeCache.using(_.apply(tp, tparam))
}
final def varianceInType(tp: Type, considerUnchecked: Boolean = false)(tparam: Symbol): Variance =
varianceInTypeCache.using(_.apply(tp, tparam, considerUnchecked))

private[this] val varianceInTypeCache = ReusableInstance[varianceInType](new varianceInType)

private final class varianceInType {
private[this] var tp: Type = _
private[this] var tparam: Symbol = _
private[this] var considerUnchecked = false

import Variance._
private def inArgs(sym: Symbol, args: List[Type]): Variance = foldExtract2(args, sym.typeParams)(inArgParam)
private def inSyms(syms: List[Symbol]): Variance = foldExtract(syms)(inSym)
private def inTypes(tps: List[Type]): Variance = foldExtract(tps)(inType)
private def inAnnots(anns: List[AnnotationInfo]): Variance = foldExtract(anns)(inAnnotationAtp)
private def unchecked(anns: List[AnnotationInfo]): Boolean = considerUnchecked && anns.exists(_.matches(definitions.uncheckedVarianceClass))

// OPT these extractors are hoisted to fields to reduce allocation. We're also avoiding Function1[_, Variance] to
// avoid value class boxing.
Expand All @@ -300,17 +302,20 @@ trait Variances {
case MethodType(params, restpe) => inSyms(params).flip & inType(restpe)
case PolyType(tparams, restpe) => inSyms(tparams).flip & inType(restpe)
case ExistentialType(tparams, restpe) => inSyms(tparams) & inType(restpe)
case AnnotatedType(annots, _) if unchecked(annots) => Bivariant
case AnnotatedType(annots, tp) => inAnnots(annots) & inType(tp)
case SuperType(thistpe, supertpe) => inType(thistpe) & inType(supertpe)
}

def apply(tp: Type, tparam: Symbol): Variance = {
def apply(tp: Type, tparam: Symbol, considerUnchecked: Boolean): Variance = {
this.tp = tp
this.tparam = tparam
this.considerUnchecked = considerUnchecked
try inType(tp)
finally {
this.tp = null
this.tparam = null
this.considerUnchecked = false
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions test/files/pos/variance-holes.scala
@@ -1,3 +1,5 @@
import scala.annotation.unchecked.uncheckedVariance

object Test {
type Lower1[+A] >: List[A]

Expand Down Expand Up @@ -44,4 +46,16 @@ object Test {
}

def generic[A]: Unit = ()

trait UncheckedHKT {
type F[+_, -_]
}

object UncheckedHKT {
def impl1[G[_, _]]: UncheckedHKT { type F[+A, -B] = G[A @uncheckedVariance, B @uncheckedVariance] } =
new UncheckedHKT { type F[+A, -B] = G[A @uncheckedVariance, B @uncheckedVariance] }

def impl2[G[_, _]]: UncheckedHKT { type F[+A, -B] = G[A, B] @uncheckedVariance } =
new UncheckedHKT { type F[+A, -B] = G[A, B] @uncheckedVariance }
}
}

0 comments on commit bacd612

Please sign in to comment.