class A has one type parameter, but type B has one

前端 未结 2 590
傲寒
傲寒 2021-02-12 22:34

Recently I stumbled across a strange (to me) compiler error message. Consider the following code:

trait Foo {
  type Res <: Foo
  type Bar[X <: Res]
}

cla         


        
2条回答
  •  后悔当初
    2021-02-12 22:45

    If we look to the Scala compiler, the sources could help us understanding what the problem is. I have never contributed to the Scala compiler, but I found the sources very readable and I have already investigated on that.

    The class responsible for type inference is scala.tools.nsctypechecker.Infer which you can find simply by looking in the Scala compiler sources for a part of your error. You'll find out the following fragment:

      /** error if arguments not within bounds. */
        def checkBounds(pos: Position, pre: Type, owner: Symbol,
                        tparams: List[Symbol], targs: List[Type], prefix: String) = {
          //@M validate variances & bounds of targs wrt variances & bounds of tparams
          //@M TODO: better place to check this?
          //@M TODO: errors for getters & setters are reported separately
          val kindErrors = checkKindBounds(tparams, targs, pre, owner)
    
          if(!kindErrors.isEmpty) {
            error(pos,
              prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") +
              " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." +
              kindErrors.toList.mkString("\n", ", ", ""))
          } 
    

    So now the point is understanding why checkKindBounds(tparams, targs, pre, owner) returns those errors. If you go down the method call chain, you will see that the checkKindBounds call another method

    val errors = checkKindBounds0(tparams, targs, pre, owner, true)
    

    You'll see the problem is connected to checking bounds of higher-kinded type, at line 5784, inside checkKindBoundsHK :

     if (!sameLength(hkargs, hkparams)) {
            if (arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded
            else {error = true; (List((arg, param)), Nil, Nil) } // shortcut: always set error, whether explainTypesOrNot
          }
    

    The test is not passed, it appears that in my debugger:

    hkargs$1 = {scala.collection.immutable.Nil$@2541}"List()"
    arg$1 = {scala.tools.nsc.symtab.Symbols$ClassSymbol@2689}"class List"
    param$1 = {scala.tools.nsc.symtab.Symbols$TypeSymbol@2557}"type B"
    paramowner$1 = {scala.tools.nsc.symtab.Symbols$MethodSymbol@2692}"method process"
    underHKParams$1 = {scala.collection.immutable.$colon$colon@2688}"List(type R)"
    withHKArgs$1 = {scala.collection.immutable.Nil$@2541}"List()"
    exceptionResult12 = null
    hkparams$1 = {scala.collection.immutable.$colon$colon@2688}"List(type R)"
    

    So it appears like there is one higher kinded param, type R, but there is no provided value for that.

    If you actually go back to the to checkKindBounds, you see that after the snippet:

     val (arityMismatches, varianceMismatches, stricterBounds) = (
            // NOTE: *not* targ.typeSymbol, which normalizes
            checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO)
          )
    

    the arityMismatches contains a tuple List, B. And now you can also see that the error message is wrong:

    inferred kinds of the type arguments (MyFoo,MyFoo,List[X]) do not conform to the expected kinds of the type parameters (type F,type R,type B). List[X]'s type parameters do not match type B's expected parameters: class List has one type parameter, but type B has ZERO

    In fact if you put a breakpoint at line 5859 on the following call

    checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO)
    

    you can see that

    tparam = {scala.tools.nsc.symtab.Symbols$TypeSymbol@2472}"type B"
    targ = {scala.tools.nsc.symtab.Types$UniqueTypeRef@2473}"List[X]"
    

    Conclusion:

    For some reason, when dealing with complex higher-kinded types such as yours, Scala compiler inference is limited. I don't know where it does come from, maybe you want to send a bug to the compiler team

提交回复
热议问题