Skip to content

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

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

Closed
rssh opened this issue Jun 28, 2020 · 3 comments · Fixed by #11029
Closed
Assignees

Comments

@rssh
Copy link
Contributor

rssh commented Jun 28, 2020

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.

@rssh rssh added the itype:bug label Jun 28, 2020
@rssh
Copy link
Contributor Author

rssh commented Jun 28, 2020

dotty-cps-minimized-break-lo.tar.gz

attached a full archive with sbt files, which shows this error when run 'test:compile'.

@rssh
Copy link
Contributor Author

rssh commented Jun 30, 2020

btw, in TypeComparison we compare tp1 with TypeNothing via eq. Not sure, that we systematically normalize all possible TypeNothing entries in constructors.
Nothing, which come from bound:

t1=TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),module scala),Nothing)

and what NothingType:

NothingType=TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Nothing)

@nicolasstucki
Copy link
Contributor

Partial minimization and workaround.

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[Y]:PartialFunction[Any,Y] = ???
}

object Async {

  class InferAsyncArg[F[_]](using am:CpsMonad[F]) {
    inline def apply[T](inline expr: T):Any = ${ Async.transformImpl[F,T]('expr) }
  }

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

    f.unseal match
      case Inlined(_,_,Block(_,Apply(TypeApply(Select(q,n),tparams),List(param)))) =>
        param.tpe match
          case AppliedType(tp,tparams1) =>
            val toWrapped = AppliedType(summon[quoted.Type[F]].unseal.tpe, List(tparams1.tail.head)) // issue seems to be here
            // val toWrapped = // workaround
            //   tparams1.tail.head.asInstanceOf[Type].seal match
            //     case '[$t] => '[F[$t]].unseal.tpe.asInstanceOf[Type]
            toWrapped.seal match
              case '[$wrappedTp] =>
                println(wrappedTp.show)
                '{ cps.PFHelper.create[$wrappedTp] }
}

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jan 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants