Skip to content

boundsViolation of lower bound 'Nothing' when constructing a partial function from tasty. #9254

Closed
@rssh

Description

@rssh

Minimized code

file 1:

package cps

import scala.quoted._

trait CpsMonad[F[_]]

trait ComputationBound[T]

implicit object ComputationBoundMonad extends CpsMonad[ComputationBound]

inline def async[F[_]](using am:CpsMonad[F]): Async.InferAsyncArg[F] =
   new Async.InferAsyncArg[F]

object PFHelper {
  def create[X,Y](x:Boolean):PartialFunction[X,Y] = ???
}

object Async {

  class InferAsyncArg[F[_]](using am:CpsMonad[F]) {

       inline def apply[T](inline expr: T):Unit =
       ${
         Async.transformImpl[F,T]('expr)
        }

  }

  def transformImpl[F[_]:Type,T:Type](f: Expr[T])(using qctx: QuoteContext): Expr[Unit] =
    import qctx.tasty.{_,given _}

    def uninline(t:Term):Term =
      t match
        case Inlined(_,_,x) => uninline(x)
        case _ => t

    val fu = uninline(f.unseal)
    fu match
      case Block(_,Apply(TypeApply(Select(q,n),tparams),List(param))) =>
        param.tpe match
          case AppliedType(tp,tparams1) =>
            val fromTypeOrBounds = tparams1.head
            val fromType = fromTypeOrBounds match
                 case bounds: TypeBounds => bounds.low
                 case tp: Type => tp
                 case np: NoPrefix => ???
            val toType = tparams1.tail.head
            val fType = summon[quoted.Type[F]]
            val toWrapped = AppliedType(fType.unseal.tpe,List(toType))
            val helper = '{ cps.PFHelper }.unseal
            val helperSelect = Select.unique(helper,"create")
            val createPF = Apply(
                             TypeApply(helperSelect,List(Inferred(fromType),Inferred(toWrapped))),
                             List(Literal(Constant(true)))
                           )
            val createPfApply = Apply(Select.unique(createPF,"apply"),List(Literal(Constant(1))))
            Block(List(createPfApply),Literal(Constant(()))).seal.asInstanceOf[Expr[Unit]]

}

file2:

package cps

     val c = async[ComputationBound]{
          List(1,2,3,4).collectFirst{ case x if x > 0 => x > 3 }
     }

Output

[error]   |Type argument cps.ComputationBound/T[Boolean/T] does not conform to lower bound Nothing/T
[error]   |Constraint(
[error]   | uninstVars = ;
[error]   | constrained types = 
[error]   | bounds = 
[error]   | ordering = 
[error]   |)
[error]   |Subtype trace:
[error]   |  ==> cps.ComputationBound/T[Boolean/T] <:< Nothing/T  
[error]   |    ==> cps.ComputationBound/T[Boolean/T] <:< Nothing/T recur 
[error]   |      ==> Any/T <:< Nothing/T recur 
[error]   |      <== Any/T <:< Nothing/T recur  = false
[error]   |    <== cps.ComputationBound/T[Boolean/T] <:< Nothing/T recur  = false
[error]   |  <== cps.ComputationBound/T[Boolean/T] <:< Nothing/T   = false
[error]   | This location contains code that was inlined from TestCBS1ShiftIterableOps.scala:3

Note, that error is about lower-bound Nothing, but type trace shows the opposite.

If we will print an expression in TypeOps:
'compiler/src/dotty/tools/dotc/core/TypeOps.scala'
near line 585 we will see, that violation is created, when

! (loBound <:< hi)

where

 loBound=TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),module scala),Nothing), 
hi=AppliedType(HKTypeLambda(List(T), List(TypeBounds(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),module scala),Nothing),TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),module scala),Any))), AppliedType(TypeRef(ThisType(TypeRef(NoPrefix,module class cps)),trait ComputationBound),List(TypeParamRef(T)))),List(TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Boolean)))

which is quite strange.
(will think, how to pass debug flag into <:< )

Expectation

should be compiled.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions