7mind / izumi

Productivity-oriented collection of lightweight fancy stuff for Scala toolchain
https://izumi.7mind.io
BSD 2-Clause "Simplified" License
615 stars 66 forks source link

Cannot bind inner classes #221

Closed pshirshov closed 6 years ago

pshirshov commented 6 years ago
Caused by: scala.ScalaReflectionException: class Ctx is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror
    at scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$abort(JavaMirrors.scala:116)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.ErrorInnerClass(JavaMirrors.scala:118)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.reflectClass(JavaMirrors.scala:184)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.reflectClass(JavaMirrors.scala:55)
    at com.github.pshirshov.izumi.distage.provisioning.strategies.ClassStrategyDefaultImpl.instantiateClass(ClassStrategyDefaultImpl.scala:35)
    at com.github.pshirshov.izumi.distage.provisioning.ProvisionerDefaultImpl.execute(ProvisionerDefaultImpl.scala:103)
    at com.github.pshirshov.izumi.distage.provisioning.ProvisionerDefaultImpl.$anonfun$provision$2(ProvisionerDefaultImpl.scala:33)
    at scala.util.Try$.apply(Try.scala:209)
    at com.github.pshirshov.izumi.distage.provisioning.ProvisionerDefaultImpl.$anonfun$provision$1(ProvisionerDefaultImpl.scala:33)
    ... 55 more
neko-kai commented 6 years ago

Static bindings can already bind class inner and object inner types, but not function inner types. see: https://github.com/pshirshov/izumi-r2/blob/develop/distage/distage-static/src/test/scala/com/github/pshirshov/izumi/distage/StaticInjectorTest.scala#L361

Runtime implementation unclear: need to get and carry around an instance mirror of defining value. Has to be done at level of ModuleDef, since pointer to instance is lost after. I'm not sure it's viable to solve for the runtime provisioner

pshirshov commented 6 years ago

In case we can't solve it we should fail fast at least (better during compilation)

neko-kai commented 6 years ago

The issue is two-fold

  1. Instantiating inner classes – Only a problem for reflection-based provider, distage-static works as usual – we get an error "X is an inner class, use instance mirror to instantiate". The only way to capture instance is with a macro, really... We CAN do that, as part of Tag/TagK we can detect path-dependent types and include a (weak?) reference to path parent.
  2. Type equality – when exploring an inner class that contains arguments or results from the same parent trait, they are for some reason not instantiated with their path. So =:= checks do not work. This happens in macros too, with factories. Look at StaticInnerClassesTest."progression test: compile-time ReflectionProvider can't handle factories inside stable objects that contain inner classes from inherited traits that depend on types defined inside trait", or in:
trait X {
  trait D
  case class Class(d: Dep)
  trait Factory {
    def mkClass(d: Dep): Class
   }
}
object x extends X

We get error that in mkClass parameter d is not used in the constructor of Class, even though it's used! This may happen if one of (but not both) is true:

Either way the d: Dep in mkClass doesn't match d: Dep in Class, one of them is not given a correct path.

We can probably solve this easily by keeping track of paths and using .asSeenFrom in ReflectionProvider

neko-kai commented 6 years ago

Initially we can just support only top-level object parents. The changes to do this are minimal: Just add logic in ClassStrategy, TraitStrategy and FactoryStrategy to lookup and get parent object via moduleMirror

neko-kai commented 6 years ago

Closed in https://github.com/pshirshov/izumi-r2/pull/400