Skip to content

Commit

Permalink
Removed AnyConstructor, since it's now the same as ClassConstructor
Browse files Browse the repository at this point in the history
  • Loading branch information
neko-kai committed Oct 23, 2023
1 parent 5e4ee5a commit 08d527c
Show file tree
Hide file tree
Showing 25 changed files with 260 additions and 276 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,20 @@ import zio.ZEnvironment

import scala.language.experimental.macros as enableMacros

sealed trait AnyConstructorBase[T] extends Any with ClassConstructorOptionalMakeDSL[T] {
def provider: Functoid[T]
}

/**
* An implicitly summonable constructor for a type `T`, can generate constructors for:
*
* - concrete classes (using [[ClassConstructor]])
*
* Since version `1.2.0`, does not generate constructors for traits and abstract classes, or for "factory-like" traits
* and abstract classes, instead use [[TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits]])
* and [[FactoryConstructor]] respectively. ([[https://izumi.7mind.io/distage/basics#auto-factories Auto-Factories]])
*
* Use [[ZEnvConstructor]] to generate constructors for `zio.ZEnvironment` values.
* An implicitly summonable constructor for a concrete class `T`
*
* @example
* {{{
* import distage.{AnyConstructor, Functoid, Injector, ModuleDef}
* import distage.{ClassConstructor, Functoid, Injector, ModuleDef}
*
* class A(val i: Int)
*
* val constructor: Functoid[A] = AnyConstructor[A]
* val constructor: Functoid[A] = ClassConstructor[A]
*
* val lifecycle = Injector().produceGet[A](new ModuleDef {
* make[A].from(constructor)
Expand All @@ -43,22 +39,7 @@ import scala.language.experimental.macros as enableMacros
*
* @return [[izumi.distage.model.providers.Functoid]][T] value
*/
sealed trait AnyConstructor[T] extends Any with AnyConstructorOptionalMakeDSL[T] {
def provider: Functoid[T]
}

object AnyConstructor {
def apply[T](implicit ctor: AnyConstructor[T]): Functoid[T] = ctor.provider

implicit def materialize[T]: AnyConstructor[T] = macro AnyConstructorMacro.mkAnyConstructor[T]
}

/**
* An implicitly summonable constructor for a concrete class `T`
*
* @see [[AnyConstructor]]
*/
final class ClassConstructor[T](val provider: Functoid[T]) extends AnyVal with AnyConstructor[T]
final class ClassConstructor[T](val provider: Functoid[T]) extends AnyVal with AnyConstructorBase[T]

object ClassConstructor {
def apply[T](implicit ctor: ClassConstructor[T]): Functoid[T] = ctor.provider
Expand All @@ -71,9 +52,10 @@ object ClassConstructor {
*
* @see [[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]]
* @see [[izumi.distage.model.definition.impl]] recommended documenting annotation for use with [[TraitConstructor]]
* @see [[AnyConstructor]]
*
* @return [[izumi.distage.model.providers.Functoid]][T] value
*/
final class TraitConstructor[T](val provider: Functoid[T]) extends AnyVal with AnyConstructor[T]
final class TraitConstructor[T](val provider: Functoid[T]) extends AnyVal with AnyConstructorBase[T]

object TraitConstructor {
def apply[T](implicit ctor: TraitConstructor[T]): Functoid[T] = ctor.provider
Expand All @@ -95,9 +77,10 @@ object TraitConstructor {
*
* @see [[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]]
* @see [[izumi.distage.model.definition.impl]] recommended documenting annotation for use with [[FactoryConstructor]]
* @see [[AnyConstructor]]
*
* @return [[izumi.distage.model.providers.Functoid]][T] value
*/
final class FactoryConstructor[T](val provider: Functoid[T]) extends AnyVal with AnyConstructor[T]
final class FactoryConstructor[T](val provider: Functoid[T]) extends AnyVal with AnyConstructorBase[T]

object FactoryConstructor {
def apply[T](implicit ctor: FactoryConstructor[T]): Functoid[T] = ctor.provider
Expand All @@ -111,9 +94,8 @@ object FactoryConstructor {
* `zio.ZEnvironment` heterogeneous map values may be used by ZIO or other Reader-like effects
*
* @see [[https://izumi.7mind.io/distage/basics.html#zio-environment-bindings ZIO Environment bindings]]
* @see [[AnyConstructor]]
*/
final class ZEnvConstructor[T](val provider: Functoid[ZEnvironment[T]]) extends AnyVal with AnyConstructor[ZEnvironment[T]]
final class ZEnvConstructor[T](val provider: Functoid[ZEnvironment[T]]) extends AnyVal with AnyConstructorBase[ZEnvironment[T]]

object ZEnvConstructor {
def apply[T](implicit ctor: ZEnvConstructor[T]): Functoid[ZEnvironment[T]] = ctor.provider
Expand All @@ -123,19 +105,19 @@ object ZEnvConstructor {
implicit def materialize[T]: ZEnvConstructor[T] = macro ZEnvConstructorMacro.mkZEnvConstructor[T]
}

private[constructors] sealed trait AnyConstructorOptionalMakeDSL[T] extends Any {
private[constructors] sealed trait ClassConstructorOptionalMakeDSL[T] extends Any {
def provider: Functoid[T]
}

object AnyConstructorOptionalMakeDSL {
private[constructors] final class Impl[T](val provider: Functoid[T]) extends AnyVal with AnyConstructorOptionalMakeDSL[T]
object ClassConstructorOptionalMakeDSL {
private[constructors] final class Impl[T](val provider: Functoid[T]) extends AnyVal with ClassConstructorOptionalMakeDSL[T]

@inline def apply[T](functoid: Functoid[T]): AnyConstructorOptionalMakeDSL.Impl[T] = {
new AnyConstructorOptionalMakeDSL.Impl[T](functoid)
@inline def apply[T](functoid: Functoid[T]): ClassConstructorOptionalMakeDSL.Impl[T] = {
new ClassConstructorOptionalMakeDSL.Impl[T](functoid)
}

def errorConstructor[T](tpe: String, nonWhitelistedMethods: List[String]): AnyConstructorOptionalMakeDSL.Impl[T] = {
AnyConstructorOptionalMakeDSL[T](Functoid.lift(throwError(tpe, nonWhitelistedMethods)))
def errorConstructor[T](tpe: String, nonWhitelistedMethods: List[String]): ClassConstructorOptionalMakeDSL.Impl[T] = {
ClassConstructorOptionalMakeDSL[T](Functoid.lift(throwError(tpe, nonWhitelistedMethods)))
}

def throwError(tpe: String, nonWhitelistedMethods: List[String]): Nothing = {
Expand All @@ -150,5 +132,5 @@ object AnyConstructorOptionalMakeDSL {
)
}

implicit def materialize[T]: AnyConstructorOptionalMakeDSL.Impl[T] = macro AnyConstructorMacro.anyConstructorOptionalMakeDSL[T]
implicit def materialize[T]: ClassConstructorOptionalMakeDSL.Impl[T] = macro MakeMacro.classConstructorOptionalMakeDSL[T]
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,55 @@ object ClassConstructorMacro {
def mkClassConstructor[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[ClassConstructor[T]] = {
import c.universe._

val macroUniverse = StaticDIUniverse(c)
val reflectionProvider = ReflectionProviderDefaultImpl(macroUniverse)

val targetType = ReflectionUtil.norm(c.universe: c.universe.type)(weakTypeOf[T].dealias)
requireConcreteTypeConstructor(c)("ClassConstructor", targetType)

(targetType match {
case t: SingletonTypeApi =>
val functoid = symbolOf[Functoid.type].asClass.module
val term = t match {
case t: ThisTypeApi => This(t.sym)
case t: ConstantTypeApi => q"${t.value}"
case _ => q"${t.termSymbol}"
}
c.Expr[ClassConstructor[T]] {
q"{ new ${weakTypeOf[ClassConstructor[T]]}($functoid.singleton[$targetType]($term)) }"
}

case _ =>
val macroUniverse = StaticDIUniverse(c)
val impls = ClassConstructorMacros(c)(macroUniverse)
import impls.{c => _, u => _, _}

val reflectionProvider = ReflectionProviderDefaultImpl(macroUniverse)
if (!reflectionProvider.isConcrete(targetType)) {
c.abort(
c.enclosingPosition,
s"""Tried to derive constructor function for class $targetType, but the class is an
|abstract class or a trait! Only concrete classes (`class` keyword) are supported""".stripMargin,
)
}

val logger = TrivialMacroLogger.make[this.type](c, DebugProperties.`izumi.debug.macro.distage.constructors`.name)

val provider: c.Expr[Functoid[T]] = mkClassConstructorProvider(reflectionProvider)(targetType)

val res = c.Expr[ClassConstructor[T]](q"{ new ${weakTypeOf[ClassConstructor[T]]}($provider) }")
logger.log(s"Final syntax tree of class for $targetType:\n$res")
res
}): @nowarn("msg=outer reference")
if (reflectionProvider.isConcrete(targetType)) {
(targetType match {
case t: SingletonTypeApi =>
val functoid = symbolOf[Functoid.type].asClass.module
val term = t match {
case t: ThisTypeApi => This(t.sym)
case t: ConstantTypeApi => q"${t.value}"
case _ => q"${t.termSymbol}"
}
c.Expr[ClassConstructor[T]] {
q"{ new ${weakTypeOf[ClassConstructor[T]]}($functoid.singleton[$targetType]($term)) }"
}

case _ =>
val impls = ClassConstructorMacros(c)(macroUniverse)

val provider: c.Expr[Functoid[T]] = impls.mkClassConstructorProvider(reflectionProvider)(targetType)

val res = c.Expr[ClassConstructor[T]](q"{ new ${weakTypeOf[ClassConstructor[T]]}($provider) }")

val logger = TrivialMacroLogger.make[this.type](c, DebugProperties.`izumi.debug.macro.distage.constructors`.name)
logger.log(s"Final syntax tree of class for $targetType:\n$res")

res
}): @nowarn("msg=outer reference")
} else if (reflectionProvider.isWireableAbstract(targetType)) {
c.abort(
c.enclosingPosition,
s"ClassConstructor failure: $targetType is a trait or an abstract class, use `makeTrait` or `fromTrait` to wire traits.",
)
} else if (reflectionProvider.isFactory(targetType)) {
c.abort(
c.enclosingPosition,
s"ClassConstructor failure: $targetType is a Factory, use `makeFactory` or `fromFactory` to wire factories.",
)
} else {
c.abort(
c.enclosingPosition,
s"""ClassConstructor failure: couldn't derive a constructor for $targetType!
|It's neither a concrete class, nor a wireable trait or abstract class!""".stripMargin,
)
}

}

}
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
package izumi.distage.constructors.macros

import scala.annotation.nowarn
import izumi.distage.constructors.{AnyConstructor, AnyConstructorOptionalMakeDSL, DebugProperties}
import izumi.distage.constructors.{ClassConstructorOptionalMakeDSL, DebugProperties}
import izumi.distage.model.definition.dsl.ModuleDefDSL
import izumi.distage.model.reflection.universe.StaticDIUniverse
import izumi.distage.reflection.ReflectionProviderDefaultImpl
import izumi.fundamentals.reflection.{ReflectionUtil, TrivialMacroLogger}
import izumi.fundamentals.reflection.TrivialMacroLogger

import scala.annotation.nowarn
import scala.reflect.api.Universe
import scala.reflect.macros.blackbox

@nowarn("msg=deprecated.*since 2.11")
object AnyConstructorMacro {
object MakeMacro {

def make[B[_], T: c.WeakTypeTag](c: blackbox.Context): c.Expr[B[T]] = {
import c.universe._
c.Expr[B[T]](q"""${c.prefix}._make[${weakTypeOf[T]}](${c.inferImplicitValue(weakTypeOf[AnyConstructorOptionalMakeDSL[T]], silent = false)}.provider)""")
import c.universe.*
c.Expr[B[T]](q"""${c.prefix}._make[${weakTypeOf[T]}](${c.inferImplicitValue(weakTypeOf[ClassConstructorOptionalMakeDSL[T]], silent = false)}.provider)""")
}

def anyConstructorOptionalMakeDSL[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[AnyConstructorOptionalMakeDSL.Impl[T]] = {
import c.universe._
def classConstructorOptionalMakeDSL[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[ClassConstructorOptionalMakeDSL.Impl[T]] = {
import c.universe.*

val logger = TrivialMacroLogger.make[this.type](c, DebugProperties.`izumi.debug.macro.distage.constructors`.name)

val enclosingClass = c.enclosingClass
// We expect this macro to be called only and __ONLY__ from `AnyConstructorMacro.make`
// We expect this macro to be called only and __ONLY__ from `MakeMacro.make`
// we're going to use the position of the `make` call to search for subsequent methods
// instead of the position of the implicit search itself (which is unstable and
// sometimes doesn't exist, for example during scaladoc compilation)
Expand Down Expand Up @@ -62,12 +60,12 @@ object AnyConstructorMacro {

val tpe = weakTypeOf[T]

c.Expr[AnyConstructorOptionalMakeDSL.Impl[T]] {
c.Expr[ClassConstructorOptionalMakeDSL.Impl[T]] {
maybeNonwhiteListedMethods match {
case None =>
c.abort(
c.enclosingPosition,
s"""Couldn't find position of the `make` call when summoning AnyConstructorOptionalMakeDSL[$tpe]
s"""Couldn't find position of the `make` call when summoning ClassConstructorOptionalMakeDSL[$tpe]
|Got tree: $maybeTree
|Result of search: $maybeNonwhiteListedMethods
|Searched for position: $positionOfMakeCall
Expand All @@ -79,45 +77,14 @@ object AnyConstructorMacro {
if (nonwhiteListedMethods.isEmpty) {
logger.log(s"""For $tpe found no `.from`-like calls in $maybeTree""".stripMargin)

q"""_root_.izumi.distage.constructors.AnyConstructorOptionalMakeDSL.apply[$tpe](${mkAnyConstructor[T](c)}.provider)"""
q"""_root_.izumi.distage.constructors.ClassConstructorOptionalMakeDSL.apply[$tpe](${ClassConstructorMacro.mkClassConstructor[T](c)}.provider)"""
} else {
logger.log(s"For $tpe found `.from`-like calls, generating ERROR constructor: $nonwhiteListedMethods")

q"""_root_.izumi.distage.constructors.AnyConstructorOptionalMakeDSL.errorConstructor[$tpe](${tpe.toString}, $nonwhiteListedMethods)"""
q"""_root_.izumi.distage.constructors.ClassConstructorOptionalMakeDSL.errorConstructor[$tpe](${tpe.toString}, $nonwhiteListedMethods)"""
}
}
}
}

def mkAnyConstructor[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[AnyConstructor[T]] = {
import c.universe._

val macroUniverse = StaticDIUniverse(c)
val reflectionProvider = ReflectionProviderDefaultImpl(macroUniverse)

val targetType = ReflectionUtil.norm(c.universe: c.universe.type)(weakTypeOf[T].dealias)
requireConcreteTypeConstructor(c)("AnyConstructor", targetType)

if (reflectionProvider.isConcrete(targetType)) {
ClassConstructorMacro.mkClassConstructor[T](c)
} else if (reflectionProvider.isWireableAbstract(targetType)) {
c.abort(
c.enclosingPosition,
s"AnyConstructor failure: $targetType is a trait or an abstract class, use makeTrait or fromTrait to wire traits.",
)
} else if (reflectionProvider.isFactory(targetType)) {
c.abort(
c.enclosingPosition,
s"AnyConstructor failure: $targetType is a Factory, use makeFactory or fromFactory to wire factories.",
)
} else {
c.abort(
c.enclosingPosition,
s"""AnyConstructor failure: couldn't generate a constructor for $targetType!
|It's neither a concrete class, nor a wireable trait or abstract class!""".stripMargin,
)
}

}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package izumi.distage.model.definition.dsl

import izumi.distage.constructors.macros.AnyConstructorMacro
import izumi.distage.constructors.macros.MakeMacro

import scala.language.experimental.macros

trait AbstractBindingDefDSLMacro[BindDSL[_]] {
final protected[this] def make[T]: BindDSL[T] = macro AnyConstructorMacro.make[BindDSL, T]
final protected[this] def make[T]: BindDSL[T] = macro MakeMacro.make[BindDSL, T]
}
Loading

0 comments on commit 08d527c

Please sign in to comment.