@@ -93,10 +93,11 @@ trait Types
9393
9494 private final val traceTypeVars = sys.props contains " scalac.debug.tvar"
9595 private final val breakCycles = settings.breakCycles.value
96- /** In case anyone wants to turn off type parameter bounds being used
96+ /** In case anyone wants to turn on type parameter bounds being used
9797 * to seed type constraints.
9898 */
9999 private final val propagateParameterBoundsToTypeVars = sys.props contains " scalac.debug.prop-constraints"
100+ private final val sharperSkolems = sys.props contains " scalac.experimental.sharper-skolems"
100101
101102 protected val enableTypeVarExperimentals = settings.Xexperimental .value
102103
@@ -2551,56 +2552,58 @@ trait Types
25512552 override def baseTypeSeq = underlying.baseTypeSeq map maybeRewrap
25522553 override def isHigherKinded = false
25532554
2555+ // TODO: check invariant that all quantifiers have the same (existing) owner
2556+ private def quantifierOwner = quantified collectFirst { case q if q.owner.exists => q.owner } getOrElse NoSymbol
2557+
2558+ // Is this existential of the form: T[Q1, ..., QN] forSome { type Q1 >: L1 <: U1, ..., QN >: LN <: UN}
2559+ private def isStraightApplication = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a }
2560+
25542561 /** [SI-6169, SI-8197 -- companion to SI-1786]
25552562 *
2556- * Approximation to improve the bounds of a Java-defined existential type,
2557- * based on the bounds of the type parameters of the quantified type
2558- * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_]
2559- * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does:
2560- * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types
2563+ * Approximation to improve the bounds of a Java-defined existential type,
2564+ * based on the bounds of the type parameters of the quantified type
2565+ * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_]
2566+ * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does:
2567+ * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types
25612568 *
2562- * (Also tried doing this once during class file parsing or when creating the existential type,
2563- * but that causes cyclic errors because it happens too early.)
2569+ * Also tried doing this once during class file parsing or when creating the existential type,
2570+ * but that causes cyclic errors because it happens too early.
2571+ *
2572+ * NOTE: we're only modifying the skolems to avoid leaking the sharper bounds to `quantified` (SI-8283)
25642573 *
25652574 * TODO: figure out how to do this earlier without running into cycles, so this can subsume the fix for SI-1786
25662575 */
2567- private def sharpenQuantifierBounds (): Unit = {
2568- /* Check that we're looking at rawToExistential's handiwork
2569- * (`existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe)))`).
2570- * We can't do this sharpening there because we'll run into cycles.
2571- */
2572- def rawToExistentialCreatedMe = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a }
2573-
2574- if (underlying.typeSymbol.isJavaDefined && rawToExistentialCreatedMe) {
2575- val tpars = underlying.typeSymbol.initialize.typeParams // TODO: is initialize needed?
2576- debuglog(s " sharpen bounds: $this | ${underlying.typeArgs.map(_.typeSymbol)} <-- ${tpars.map(_.info)}" )
2577-
2578- foreach2(quantified, tpars) { (quant, tparam) =>
2579- // TODO: check `tparam.info.substSym(tpars, quantified) <:< quant.info` instead (for some weird reason not working for test/t6169/ExistF)
2580- // for now, crude approximation for the common case
2581- if (quant.info.bounds.isEmptyBounds && ! tparam.info.bounds.isEmptyBounds) {
2582- // avoid creating cycles [pos/t2940] that consist of an existential quantifier's
2583- // bounded by an existential type that unhygienically has that quantifier as its own quantifier
2584- // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now)
2585- if ((existentialsInType(tparam.info) intersect quantified).isEmpty)
2586- quant setInfo tparam.info.substSym(tpars, quantified)
2587- }
2588- }
2589- }
2576+ override def skolemizeExistential (owner0 : Symbol , origin : AnyRef ) = {
2577+ val owner = owner0 orElse quantifierOwner
25902578
2591- _sharpenQuantifierBounds = false
2592- }
2593- private [this ] var _sharpenQuantifierBounds = true
2594-
2595- override def skolemizeExistential (owner : Symbol , origin : AnyRef ) = {
25962579 // do this here because it's quite close to what Java does:
25972580 // when checking subtyping/type equality, enter constraints
25982581 // derived from the existentially quantified type into the typing environment
25992582 // (aka \Gamma, which tracks types for variables and constraints/kinds for types)
26002583 // as a nice bonus, delaying this until we need it avoids cyclic errors
2601- if (_sharpenQuantifierBounds) sharpenQuantifierBounds
2584+ def tpars = underlying.typeSymbol.initialize.typeParams
2585+
2586+ def newSkolem (quant : Symbol ) = owner.newExistentialSkolem(quant, origin)
2587+ def newSharpenedSkolem (quant : Symbol , tparam : Symbol ): Symbol = {
2588+ def emptyBounds (sym : Symbol ) = sym.info.bounds.isEmptyBounds
2589+
2590+ // avoid creating cycles [pos/t2940] that consist of an existential quantifier's
2591+ // bounded by an existential type that unhygienically has that quantifier as its own quantifier
2592+ // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now)
2593+ val canSharpen = (
2594+ emptyBounds(quant) && ! emptyBounds(tparam)
2595+ && (existentialsInType(tparam.info) intersect quantified).isEmpty
2596+ )
2597+
2598+ val skolemInfo = if (! canSharpen) quant.info else tparam.info.substSym(tpars, quantified)
2599+
2600+ owner.newExistentialSkolem(quant.name.toTypeName, skolemInfo, quant.flags, quant.pos, origin)
2601+ }
2602+
2603+ val canSharpenBounds = (underlying.typeSymbol.isJavaDefined || sharperSkolems) && isStraightApplication
26022604
2603- deriveType(quantified, tparam => (owner orElse tparam.owner).newExistentialSkolem(tparam, origin))(underlying)
2605+ if (canSharpenBounds) deriveType2(quantified, tpars, newSharpenedSkolem)(underlying)
2606+ else deriveType(quantified, newSkolem)(underlying)
26042607 }
26052608
26062609 private def wildcardArgsString (qset : Set [Symbol ], args : List [Type ]): List [String ] = args map {
0 commit comments