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

handle type params in constructor #355

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,28 @@ private[macwire] class ConstructorCrimper[Q <: Quotes, T: Type](using val q: Q)(
ctor
}

lazy val constructorParamLists: Option[List[List[Symbol]]] = constructor.map(_.paramSymss)
private def constructorParamTypes(ctorType: TypeRepr): List[List[TypeRepr]] = {
ctorType match {
case MethodType(_, paramTypes, retType) =>
paramTypes.map(_.widen.simplified) :: constructorParamTypes(retType.simplified)
case _ =>
Nil
}
}

lazy val constructorParamLists: Option[List[List[(Symbol, TypeRepr)]]] = {
constructor.map { c =>
// paramSymss contains both type arg symbols (generic types) and value arg symbols
val symLists = c.paramSymss.filter(_.forall(!_.isTypeDef))
val ctorType =
if (targetType.typeArgs.isEmpty) targetType.memberType(c)
else targetType.memberType(c).appliedTo(targetType.typeArgs)
val typeLists = constructorParamTypes(ctorType)
symLists.zip(typeLists).map { case (syms, tpes) =>
syms.zip(tpes)
}
}
}

lazy val constructorArgs: Option[List[List[Term]]] = log.withBlock("Looking for targetConstructor arguments") {
constructorParamLists.map(wireConstructorParamsWithImplicitLookups)
Expand All @@ -62,35 +83,28 @@ private[macwire] class ConstructorCrimper[Q <: Quotes, T: Type](using val q: Q)(
constructorValue <- constructor
constructorArgsValue <- constructorArgs
} yield {
val constructionMethodTree: Term = Select(New(TypeIdent(targetType.typeSymbol)), constructorValue)
val constructionMethodTree: Term = {
val ctor = Select(New(TypeIdent(targetType.typeSymbol)), constructorValue)
if (targetType.typeArgs.isEmpty) ctor else ctor.appliedToTypes(targetType.typeArgs)
}
constructorArgsValue.foldLeft(constructionMethodTree)((acc: Term, args: List[Term]) => Apply(acc, args))
}
}

def wireConstructorParams(paramLists: List[List[Symbol]]): List[List[Term]] =
paramLists.map(_.map(p => dependencyResolver.resolve(p, /*SI-4751*/ paramType(p))))

def wireConstructorParamsWithImplicitLookups(paramLists: List[List[Symbol]]): List[List[Term]] = paramLists.map {
case params if params.forall(_.flags is Flags.Implicit) => params.map(resolveImplicitOrFail)
case params => params.map(p => dependencyResolver.resolve(p, /*SI-4751*/ paramType(p)))
}

private def resolveImplicitOrFail(param: Symbol): Term = Implicits.search(paramType(param)) match {
case iss: ImplicitSearchSuccess => iss.tree
case isf: ImplicitSearchFailure => report.throwError(s"Failed to resolve an implicit for [$param].")
}
def wireConstructorParams(paramLists: List[List[(Symbol, TypeRepr)]]): List[List[Term]] =
paramLists.map(_.map(p => dependencyResolver.resolve(p._1, /*SI-4751*/ p._2)))

private def paramType(param: Symbol): TypeRepr = {
// val (sym: Symbol, tpeArgs: List[Type]) = targetTypeD match {
// case TypeRef(_, sym, tpeArgs) => (sym, tpeArgs)
// case t => abort(s"Target type not supported for wiring: $t. Please file a bug report with your use-case.")
// }
// val pTpe = param.signature.substituteTypes(sym.asClass.typeParams, tpeArgs)
// if (param.asTerm.isByNameParam) pTpe.typeArgs.head else pTpe
def wireConstructorParamsWithImplicitLookups(paramLists: List[List[(Symbol, TypeRepr)]]): List[List[Term]] =
paramLists.map {
case params if params.forall(_._1.flags is Flags.Implicit) => params.map(resolveImplicitOrFail)
case params => params.map(p => dependencyResolver.resolve(p._1, /*SI-4751*/ p._2))
}

//FIXME assertion error in test inheritanceHKT.success, selfTypeHKT.success
Ref(param).tpe.widen
}
private def resolveImplicitOrFail(param: Symbol, paramType: TypeRepr): Term =
Implicits.search(paramType) match {
case iss: ImplicitSearchSuccess => iss.tree
case isf: ImplicitSearchFailure => report.throwError(s"Failed to resolve an implicit for [$param].")
}

/** In some cases there is one extra (phantom) constructor. This happens when extended trait has implicit param:
*
Expand Down
14 changes: 14 additions & 0 deletions tests/src/test/resources/test-cases/simpleGenericClass.success
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include commonHKTClasses

case class LocalA[X]()
case class LocalB[X](la: LocalA[X])

object Test {
val la = wire[LocalA[Int]]
val lb = wire[LocalB[Int]]
val a = wire[A[IO]]
val b = wire[B[IO]]
}

require(Test.lb.la == Test.la)
require(Test.b.a == Test.a)