Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect @uncheckedVariance in higher-kinded types (fixing 2.13.2 regression) #8938

Merged
merged 1 commit into from May 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 }
}
}