All Downloads are FREE. Search and download functionalities are using the official Maven repository.

si2712fix.MonkeyPatchedTypes.scala Maven / Gradle / Ivy

package scala.reflect.internal

import scala.tools.nsc.Global
import scala.reflect.internal.Depth.AnyDepth
import scala.reflect.internal.util.FreshNameCreator

class MonkeyPatchedTypes(val global: Global) {
  import global._
  import definitions._

  lazy val unifyRightToLeftClass = rootMirror.getClassIfDefined("scala.annotation.unifyRightToLeft")

  trait SI2712Unifier { self: TypeVar =>
    /** Called when a TypeVar is involved in a subtyping check.  Result is whether
     *  this TypeVar could plausibly be a [super/sub]type of argument `tp` and if so,
     *  tracks tp as a [lower/upper] bound of this TypeVar.
     *
     *  if (isLowerBound)   this typevar could be a subtype, track tp as a lower bound
     *  if (!isLowerBound)  this typevar could be a supertype, track tp as an upper bound
     *
     *  If isNumericBound is true, the subtype check is performed with weak_<:< instead of <:<.
     */
    override def registerBound(tp: Type, isLowerBound: Boolean, isNumericBound: Boolean = false): Boolean = {
      // println("regBound: "+(safeToString, debugString(tp), isLowerBound)) //@MDEBUG
      if (isLowerBound)
        assert(tp != this)

      // side effect: adds the type to upper or lower bounds
      def addBound(tp: Type) {
        if (isLowerBound) addLoBound(tp, isNumericBound)
        else addHiBound(tp, isNumericBound)
      }
      // swaps the arguments if it's an upper bound
      def checkSubtype(tp1: Type, tp2: Type) = {
        val lhs = if (isLowerBound) tp1 else tp2
        val rhs = if (isLowerBound) tp2 else tp1

        if (isNumericBound) lhs weak_<:< rhs
        else lhs <:< rhs
      }

      /*  Simple case: type arguments can be ignored, because either this typevar has
       *  no type parameters, or we are comparing to Any/Nothing.
       *
       *  The latter condition is needed because HK unification is limited to constraints of the shape
       *  {{{
       *    TC1[T1,..., TN] <: TC2[T'1,...,T'N]
       *  }}}
       *  which would preclude the following important constraints:
       *  {{{
       *    Nothing <: ?TC[?T]
       *    ?TC[?T] <: Any
       *  }}}
       */
      def unifySimple = {
        val sym = tp.typeSymbol
        if (sym == NothingClass || sym == AnyClass) { // kind-polymorphic
          // SI-7126 if we register some type alias `T=Any`, we can later end
          // with malformed types like `T[T]` during type inference in
          // `handlePolymorphicCall`. No such problem if we register `Any`.
          addBound(sym.tpe)
          true
        } else if (params.isEmpty) {
          addBound(tp)
          true
        } else false
      }

      /*  Full case: involving a check of the form
       *  {{{
       *    TC1[T1,..., TN] <: TC2[T'1,...,T'N]
       *  }}}
       *  Checks subtyping of higher-order type vars, and uses variances as defined in the
       *  type parameter we're trying to infer (the result will be sanity-checked later).
       */
      def unifyFull(tpe: Type): Boolean = {
        def unifySpecific(tp: Type) = {
          if(sameLength(typeArgs, tp.typeArgs)) {
            val lhs = if (isLowerBound) tp.typeArgs else typeArgs
            val rhs = if (isLowerBound) typeArgs else tp.typeArgs
            // This is a higher-kinded type var with same arity as tp.
            // If so (see SI-7517), side effect: adds the type constructor itself as a bound.
            isSubArgs(lhs, rhs, params, AnyDepth) && { addBound(tp.typeConstructor); true }
          } else if(/*settings.YhigherOrderUnification &&*/ typeArgs.lengthCompare(0) > 0 && compareLengths(typeArgs, tp.typeArgs) < 0) {
            // Simple algorithm as suggested by Paul Chiusano in the comments on SI-2712
            //
            //   https://issues.scala-lang.org/browse/SI-2712?focusedCommentId=61270
            //
            // Treat the type constructor as curried and partially applied, we treat a prefix
            // as constants and solve for the suffix. For the example in the ticket, unifying
            // M[A] with Int => Int this unifies as,
            //
            //   M[t] = [t][Int => t]
            //   A = Int
            //
            // A more "natural" unifier might be M[t] = [t][t => t]. There's lots of scope for
            // experimenting with alternatives here.

            val tpSym = tp.typeSymbolDirect
            val rightToLeft = tpSym.annotations.exists(_ matches unifyRightToLeftClass)

            val numAbstracted = typeArgs.length
            val numCaptured = tp.typeArgs.length-numAbstracted
            val (captured, abstracted) =
              if(rightToLeft) tp.typeArgs.splitAt(numAbstracted).swap
             else tp.typeArgs.splitAt(numCaptured)

            val lhs = if (isLowerBound) abstracted else typeArgs
            val rhs = if (isLowerBound) typeArgs else abstracted
            // This is a higher-kinded type var with same arity as tp.
            // If so (see SI-7517), side effect: adds the type constructor itself as a bound.
            isSubArgs(lhs, rhs, params, AnyDepth) && {
              val absSyms =
                if(rightToLeft) tpSym.typeParams.take(numAbstracted)
                else tpSym.typeParams.drop(numCaptured)
              val freeSyms = absSyms.map(_.cloneSymbol(tpSym))
              val args =
                if(rightToLeft) freeSyms.map(_.tpeHK) ++ captured
                else captured ++ freeSyms.map(_.tpeHK)
              val poly = PolyType(freeSyms, appliedType(tp.typeConstructor, args))
              addBound(poly)
              true
            }
          } else
            false
        }
        // The type with which we can successfully unify can be hidden
        // behind singleton types and type aliases.
        tpe.dealiasWidenChain exists unifySpecific
      }

      // There's a <: test taking place right now, where tp is a concrete type and this is a typevar
      // attempting to satisfy that test. Either the test will be unsatisfiable, in which case
      // registerBound will return false; or the upper or lower bounds of this type var will be
      // supplemented with the type being tested against.
      //
      // Eventually the types which have accumulated in the upper and lower bounds will be lubbed
      // (resp. glbbed) to instantiate the typevar.
      //
      // The only types which are eligible for unification are those with the same number of
      // typeArgs as this typevar, or Any/Nothing, which are kind-polymorphic. For the upper bound,
      // any parent or base type of `tp` may be tested here (leading to a corresponding relaxation
      // in the upper bound.) The universe of possible glbs, being somewhat more infinite, is not
      // addressed here: all lower bounds are retained and their intersection calculated when the
      // bounds are solved.
      //
      // In a side-effect free universe, checking tp and tp.parents before checking tp.baseTypeSeq
      // would be pointless. In this case, each check we perform causes us to lose specificity: in
      // the end the best we'll do is the least specific type we tested against, since the typevar
      // does not see these checks as "probes" but as requirements to fulfill.
      // TODO: can the `suspended` flag be used to poke around without leaving a trace?
      //
      // So the strategy used here is to test first the type, then the direct parents, and finally
      // to fall back on the individual base types. This warrants eventual re-examination.

      // AM: I think we could use the `suspended` flag to avoid side-effecting during unification
      if (suspended)         // constraint accumulation is disabled
        checkSubtype(tp, origin)
      else if (instValid)  // type var is already set
        checkSubtype(tp, inst)
      else isRelatable(tp) && {
        unifySimple || unifyFull(tp) || (
          // only look harder if our gaze is oriented toward Any
          isLowerBound && (
            (tp.parents exists unifyFull) || (
              // @PP: Is it going to be faster to filter out the parents we just checked?
              // That's what's done here but I'm not sure it matters.
              tp.baseTypeSeq.toList.tail filterNot (tp.parents contains _) exists unifyFull
            )
          )
        )
      }
    }

    val sm = classOf[TypeVar].getDeclaredMethod("scala$reflect$internal$Types$$suspended")
    sm.setAccessible(true)
    def suspended: Boolean = sm.invoke(this).asInstanceOf[Boolean]
  }
  
  val tm = classOf[Types].getDeclaredMethod("scala$reflect$internal$Types$$traceTypeVars")
  tm.setAccessible(true)
  def traceTypeVars: Boolean = tm.invoke(global).asInstanceOf[Boolean]

  val pm = classOf[Types].getDeclaredMethod("scala$reflect$internal$Types$$propagateParameterBoundsToTypeVars")
  pm.setAccessible(true)
  def propagateParameterBoundsToTypeVars: Boolean = pm.invoke(global).asInstanceOf[Boolean]

  //@M
  // a TypeVar used to be a case class with only an origin and a constr
  // then, constr became mutable (to support UndoLog, I guess),
  // but pattern-matching returned the original constr0 (a bug)
  // now, pattern-matching returns the most recent constr
  object TypeVar {
    @inline final def trace[T](action: String, msg: => String)(value: T): T = {
      if (traceTypeVars) {
        val s = msg match {
          case ""   => ""
          case str  => "( " + str + " )"
        }
        Console.err.println("[%10s] %-25s%s".format(action, value, s))
      }
      value
    }

    /** Create a new TypeConstraint based on the given symbol.
     */
    private def deriveConstraint(tparam: Symbol): TypeConstraint = {
      /** Must force the type parameter's info at this point
       *  or things don't end well for higher-order type params.
       *  See SI-5359.
       */
      val bounds  = tparam.info.bounds
      /* We can seed the type constraint with the type parameter
       * bounds as long as the types are concrete.  This should lower
       * the complexity of the search even if it doesn't improve
       * any results.
       */
      if (propagateParameterBoundsToTypeVars) {
        val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType)

        if (exclude) new TypeConstraint
        else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds))
      }
      else new TypeConstraint
    }
    def untouchable(tparam: Symbol): TypeVar                 = createTypeVar(tparam, untouchable = true)
    def apply(tparam: Symbol): TypeVar                       = createTypeVar(tparam, untouchable = false)
    def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil)
    def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]): TypeVar =
      createTypeVar(origin, constr, args, params, untouchable = false)

    /** This is the only place TypeVars should be instantiated.
     */
    def createTypeVar(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol], untouchable: Boolean): TypeVar = {
      val tv = (
        if (args.isEmpty && params.isEmpty) {
          if (untouchable) new TypeVar(origin, constr) with UntouchableTypeVar with SI2712Unifier
          else new TypeVar(origin, constr) with SI2712Unifier
        }
        else if (args.size == params.size) {
          if (untouchable) new AppliedTypeVar(origin, constr, params zip args) with UntouchableTypeVar with SI2712Unifier
          else new AppliedTypeVar(origin, constr, params zip args) with SI2712Unifier
        }
        else if (args.isEmpty) {
          if (untouchable) new HKTypeVar(origin, constr, params) with UntouchableTypeVar with SI2712Unifier
          else new HKTypeVar(origin, constr, params) with SI2712Unifier
        }
        else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params)))
      )

      trace("create", "In " + tv.originLocation)(tv)
    }
    def createTypeVar(tparam: Symbol, untouchable: Boolean): TypeVar =
      createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy