Skip to content
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

Extension method interferes with unification #7745

Closed
scabug opened this issue Aug 12, 2013 · 6 comments
Closed

Extension method interferes with unification #7745

scabug opened this issue Aug 12, 2013 · 6 comments
Labels

Comments

@scabug
Copy link

scabug commented Aug 12, 2013

Compiling the following produces an error only in the presence of the combination of the implicit conversion from Base to BaseOps and the use of the type alias to partially apply the binary type constructor Op. If the underlying type constructor is unary, or if the method on BaseOps is called directly then compilation succeeds.

package bug
 
import scala.language.{ higherKinds, implicitConversions }
 
class Base[T]
 
class BaseOps[T] {
  type OpT[U] = Op[T, U]          // Fails below
  //type OpT[U] = List[U]         // OK
  //trait OpT[U] extends Op[T, U] // OK
 
  def op(tgt: OpTarget[OpT]) = tgt
}
 
object Base {
  implicit def baseOps[T](b: Base[T]) = new BaseOps[T]
}
 
class Op[A, B]
 
class OpTarget[TC[_]]
 
object OpTarget {
  implicit def apply[TC[_]](a: Any): OpTarget[TC] = new OpTarget[TC]
}
 
object TestBase {
  val baseOps = new BaseOps[String]
  baseOps.op(23) // OK in all cases
 
  val base = new Base[String]
  base.op(23)  // In the failing case:
  // found   : Int(23)
  // required: shapeless.OpTarget[[U]shapeless.Op[String,U]]
  //  base.op(23)
  //          ^
}
@scabug
Copy link
Author

scabug commented Aug 12, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7745?orig=1
Reporter: @milessabin
Affected Versions: 2.10.2, 2.11.0-M4
See #5075, #2712

@scabug
Copy link
Author

scabug commented Aug 12, 2013

@retronym said:
Another okay case:

  val base = new Base[String]
  base.op(OpTarget[BaseOps[String]#OpT](23)) // OK in all cases

@scabug
Copy link
Author

scabug commented Aug 12, 2013

@retronym said:
Here's the typer trace.

% qbin/scalac -Dscala.color -Ytyper-debug sandbox/t7745.scala
...
|    |-- base.op(23) BYVALmode-EXPRmode (site: value <local TestBase> in TestBase)
|    |    |-- base.op BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local TestBase> in TestBase)
|    |    |    |-- base EXPRmode-POLYmode-QUALmode (silent: value <local TestBase> in TestBase)
|    |    |    |    \-> TestBase.base.type (with underlying type Base[String])
|    |    |    |-- Base.baseOps BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local TestBase> in TestBase) implicits disabled
|    |    |    |    [adapt] [T](b: Base[T])BaseOps[T] adapted to [T](b: Base[T])BaseOps[T]
|    |    |    |    \-> (b: Base[T])BaseOps[T]
|    |    |    solving for T/?T
|    |    |    [adapt] [T](b: Base[T])BaseOps[T] adapted to [T](b: Base[T])BaseOps[T] based on pt TestBase.base.type => ?{def op: ?}
|    |    |    |-- [T](b: Base[T])BaseOps[T] EXPRmode-POLYmode-QUALmode (silent: value <local TestBase> in TestBase)
|    |    |    |    \-> BaseOps[String]
|    |    |    |-- Base.baseOps[String](TestBase.this.base).op BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local TestBase> in TestBase)
|    |    |    |    \-> (tgt: OpTarget[[U]Op[String,U]])OpTarget[[U]Op[String,U]]
|    |    |    \-> (tgt: OpTarget[[U]Op[String,U]])OpTarget[[U]Op[String,U]]
|    |    |-- 23 : pt=OpTarget[[U]Op[String,U]] BYVALmode-EXPRmode (silent: value <local TestBase> in TestBase)
|    |    |    |-- OpTarget.apply BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local TestBase> in TestBase) implicits disabled
|    |    |    |    [adapt] [TC[_]](a: Any)OpTarget[TC] adapted to [TC[_]](a: Any)OpTarget[TC]
|    |    |    |    \-> (a: Any)OpTarget[TC]
|    |    |    solving for TC/?TC
|    |    |    [adapt] [TC[_]](a: Any)OpTarget[TC] adapted to [TC[_]](a: Any)OpTarget[TC] based on pt Int(23) => OpTarget[[U]Op[String,U]]
|    |    |    |-- OpTarget.apply BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local TestBase> in TestBase) implicits disabled
|    |    |    |    [adapt] [TC[_]](a: Any)OpTarget[TC] adapted to [TC[_]](a: Any)OpTarget[TC]
|    |    |    |    \-> (a: Any)OpTarget[TC]
|    |    |    solving for TC/?TC
|    |    |    [adapt] [TC[_]](a: Any)OpTarget[TC] adapted to [TC[_]](a: Any)OpTarget[TC] based on pt (=> Int(23)) => OpTarget[[U]Op[String,U]]
|    |    |    \-> <error>
|    |    second try: (tgt: OpTarget[BaseOps.this.OpT])OpTarget[BaseOps.this.OpT] and 23

@scabug
Copy link
Author

scabug commented Aug 12, 2013

@retronym said:
A workaround is to declare OpTarget as covariant.

% cat sandbox/t7745.scala  | egrep 'class OpTarget|base.op'
class OpTarget[+TC[_]]
  // base.op(OpTarget[BaseOps[String]#OpT](23)) // OK in all cases
  base.op(23)  // In the failing case:
  //  base.op(23)
% scalac-hash v2.10.2 sandbox/t7745.scala
[info] v2.10.2 => /Users/jason/usr/scala-v2.10.2-0-g60d462e

The bug seems to lie in Infer::prototypeArgs, which checks if the result type of the implicit apply, OpTarget[?TC] (where ?TC is a type variable), is a subtype of the expected type, OpTarget[[U]List[U]] or OpTarget[[U]Op[String,U]]. The former returns true, the latter false.

@scabug
Copy link
Author

scabug commented Aug 12, 2013

@retronym said:
I think at the end of the day this is #2712 in a new guise.

When typechecking baseOps(base).op, the compiler normalizes away the type alias in
the method signature of op. This happens (oddly enough) in checkAccessible, which
calls:

scala> val baseOps = typeOf[BaseOps[String]]
baseOps: $r.intp.global.Type = BaseOps[String]

scala> baseOps memberType baseOps.decl(TermName("op"))
res29: $r.intp.global.Type = (tgt: OpTarget[[U]Op[String,U]])OpTarget[[U]Op[String,U]]

@scabug scabug added the typer label Apr 7, 2017
@scabug scabug added this to the Backlog milestone Apr 7, 2017
@som-snytt
Copy link

Works in 2.13.x. Test at scala/scala#9657

@SethTisue SethTisue removed this from the Backlog milestone Jun 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants