Repository: 7mind/izumi
Pull request #1809 (2022-10-31 13:11:01)

Update sbt to 1.7.3
Updates [org.scala-sbt:sbt](https://github.com/sbt/sbt) from 1.7.2 to 1.7.3.
[GitHub Release Notes](https://github.com/sbt/sbt/releases/tag/v1.7.3) - [Version Diff](https://github.com/sbt/sbt/compare/v1.7.2...v1.7.3)


I'll automatically update this PR to resolve conflicts as long as you don't change it yourself.

If you'd like to skip this version, you can just close this PR. If you have any feedback, just mention me in the comments below.

Configure Scala Steward for your repository with a [`.scala-steward.conf`](https://github.com/scala-steward-org/scala-steward/blob/13d63e8ae98a714efcdac2c7af18f004130512fa/docs/repo-specific-configuration.md) file.

Have a fantastic day writing Scala!

<details>
<summary>Files still referring to the old version number</summary>

The following files still refer to the old version number (1.7.2).
You might want to review and update them manually.
```
doc/microsite/src/main/tut/media/user-logo-best-bank.svg
```
</details>
<details>
<summary>Adjust future updates</summary>

Add this to your `.scala-steward.conf` file to ignore future updates of this dependency:
```
updates.ignore = [ { groupId = "org.scala-sbt", artifactId = "sbt" } ]
```
Or, add this to slow down future updates of this dependency:
```
dependencyOverrides = [{
  pullRequests = { frequency = "@monthly" },
  dependency = { groupId = "org.scala-sbt", artifactId = "sbt" }
}]
```
</details>

labels: library-update, early-semver-patch, semver-spec-patch, old-version-remains, commit-count:1


Patch:
diff --git a/build.sbt b/build.sbt
index 38c73973f..dac293869 100644
--- a/build.sbt
+++ b/build.sbt
@@ -16,7 +16,7 @@ lazy val `fundamentals-functional` = project.in(file("fundamentals/fundamentals-
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -127,7 +127,7 @@ lazy val `fundamentals-functional` = project.in(file("fundamentals/fundamentals-
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -176,7 +176,7 @@ lazy val `fundamentals-collections` = project.in(file("fundamentals/fundamentals
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -287,7 +287,7 @@ lazy val `fundamentals-collections` = project.in(file("fundamentals/fundamentals
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -334,7 +334,7 @@ lazy val `fundamentals-literals` = project.in(file("fundamentals/fundamentals-li
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -445,7 +445,7 @@ lazy val `fundamentals-literals` = project.in(file("fundamentals/fundamentals-li
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -496,7 +496,7 @@ lazy val `fundamentals-orphans` = project.in(file("fundamentals/fundamentals-orp
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -607,7 +607,7 @@ lazy val `fundamentals-orphans` = project.in(file("fundamentals/fundamentals-orp
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -665,7 +665,7 @@ lazy val `fundamentals-language` = project.in(file("fundamentals/fundamentals-la
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -776,7 +776,7 @@ lazy val `fundamentals-language` = project.in(file("fundamentals/fundamentals-la
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -827,7 +827,7 @@ lazy val `fundamentals-platform` = project.in(file("fundamentals/fundamentals-pl
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -938,7 +938,7 @@ lazy val `fundamentals-platform` = project.in(file("fundamentals/fundamentals-pl
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1000,7 +1000,7 @@ lazy val `fundamentals-json-circe` = project.in(file("fundamentals/fundamentals-
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -1111,7 +1111,7 @@ lazy val `fundamentals-json-circe` = project.in(file("fundamentals/fundamentals-
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1163,7 +1163,7 @@ lazy val `fundamentals-reflection` = project.in(file("fundamentals/fundamentals-
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -1274,7 +1274,7 @@ lazy val `fundamentals-reflection` = project.in(file("fundamentals/fundamentals-
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1334,7 +1334,7 @@ lazy val `fundamentals-bio` = project.in(file("fundamentals/fundamentals-bio"))
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -1445,7 +1445,7 @@ lazy val `fundamentals-bio` = project.in(file("fundamentals/fundamentals-bio"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1504,7 +1504,7 @@ lazy val `distage-core-api` = project.in(file("distage/distage-core-api"))
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -1615,7 +1615,7 @@ lazy val `distage-core-api` = project.in(file("distage/distage-core-api"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1665,7 +1665,7 @@ lazy val `distage-core-proxy-bytebuddy` = project.in(file("distage/distage-core-
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -1776,7 +1776,7 @@ lazy val `distage-core-proxy-bytebuddy` = project.in(file("distage/distage-core-
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1826,7 +1826,7 @@ lazy val `distage-framework-api` = project.in(file("distage/distage-framework-ap
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -1937,7 +1937,7 @@ lazy val `distage-framework-api` = project.in(file("distage/distage-framework-ap
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -1993,7 +1993,7 @@ lazy val `distage-core` = project.in(file("distage/distage-core"))
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -2104,7 +2104,7 @@ lazy val `distage-core` = project.in(file("distage/distage-core"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -2158,7 +2158,7 @@ lazy val `distage-extension-config` = project.in(file("distage/distage-extension
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -2269,7 +2269,7 @@ lazy val `distage-extension-config` = project.in(file("distage/distage-extension
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -2322,7 +2322,7 @@ lazy val `distage-extension-logstage` = project.in(file("distage/distage-extensi
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -2433,7 +2433,7 @@ lazy val `distage-extension-logstage` = project.in(file("distage/distage-extensi
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -2487,7 +2487,7 @@ lazy val `distage-extension-plugins` = project.in(file("distage/distage-extensio
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -2598,7 +2598,7 @@ lazy val `distage-extension-plugins` = project.in(file("distage/distage-extensio
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -2660,7 +2660,7 @@ lazy val `distage-framework` = project.in(file("distage/distage-framework"))
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -2771,7 +2771,7 @@ lazy val `distage-framework` = project.in(file("distage/distage-framework"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -2830,7 +2830,7 @@ lazy val `distage-framework-docker` = project.in(file("distage/distage-framework
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -2941,7 +2941,7 @@ lazy val `distage-framework-docker` = project.in(file("distage/distage-framework
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -2990,7 +2990,7 @@ lazy val `distage-testkit-core` = project.in(file("distage/distage-testkit-core"
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -3101,7 +3101,7 @@ lazy val `distage-testkit-core` = project.in(file("distage/distage-testkit-core"
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -3159,7 +3159,7 @@ lazy val `distage-testkit-scalatest` = project.in(file("distage/distage-testkit-
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -3270,7 +3270,7 @@ lazy val `distage-testkit-scalatest` = project.in(file("distage/distage-testkit-
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -3326,7 +3326,7 @@ lazy val `logstage-core` = project.in(file("logstage/logstage-core"))
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -3437,7 +3437,7 @@ lazy val `logstage-core` = project.in(file("logstage/logstage-core"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -3492,7 +3492,7 @@ lazy val `logstage-rendering-circe` = project.in(file("logstage/logstage-renderi
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -3603,7 +3603,7 @@ lazy val `logstage-rendering-circe` = project.in(file("logstage/logstage-renderi
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -3653,7 +3653,7 @@ lazy val `logstage-adapter-slf4j` = project.in(file("logstage/logstage-adapter-s
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -3764,7 +3764,7 @@ lazy val `logstage-adapter-slf4j` = project.in(file("logstage/logstage-adapter-s
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -3818,7 +3818,7 @@ lazy val `logstage-sink-slf4j` = project.in(file("logstage/logstage-sink-slf4j")
   )
   .settings(
     crossScalaVersions := Seq(
-      "3.2.1-RC2",
+      "3.2.1",
       "2.13.8",
       "2.12.16"
     ),
@@ -3929,7 +3929,7 @@ lazy val `logstage-sink-slf4j` = project.in(file("logstage/logstage-sink-slf4j")
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -4118,7 +4118,7 @@ lazy val `microsite` = project.in(file("doc/microsite"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
@@ -4337,7 +4337,7 @@ lazy val `sbt-izumi-deps` = project.in(file("sbt-plugins/sbt-izumi-deps"))
         "-Ycache-macro-class-loader:last-modified",
         "-Wunused:-synthetics"
       )
-      case (_, "3.2.1-RC2") => Seq(
+      case (_, "3.2.1") => Seq(
         "-Yretain-trees",
         "-language:3.2",
         "-Ykind-projector:underscores",
diff --git a/distage/distage-core-api/src/main/scala-2/izumi/distage/constructors/constructors.scala b/distage/distage-core-api/src/main/scala-2/izumi/distage/constructors/constructors.scala
index a0eda638e..4c27ab2db 100644
--- a/distage/distage-core-api/src/main/scala-2/izumi/distage/constructors/constructors.scala
+++ b/distage/distage-core-api/src/main/scala-2/izumi/distage/constructors/constructors.scala
@@ -15,8 +15,10 @@ import scala.language.experimental.macros as enableMacros
   *
   *   - concrete classes (using [[ClassConstructor]])
   *   - traits and abstract classes ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]], using [[TraitConstructor]])
-  *   - "factory-like" traits and abstract classes ([[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]], using [[FactoryConstructor]])
-  *   - `zio.Has` heterogeneous map values for use by ZIO or other Reader-like effects ([[https://izumi.7mind.io/distage/basics.html#zio-has-bindings ZIO Has bindings]], using [[HasConstructor]])
+  *
+  * Since version `1.1.0`, does not generate constructors "factory-like" traits and abstract classes, instead use [[FactoryConstructor]].
+  *
+  * Use [[HasConstructor]] to generate constructors for `zio.Has` values.
   *
   * @example
   * {{{
diff --git a/distage/distage-core-api/src/main/scala-2/izumi/distage/model/providers/FunctoidMacroMethods.scala b/distage/distage-core-api/src/main/scala-2/izumi/distage/model/providers/FunctoidMacroMethods.scala
index 14b7c1cec..a81ebbc44 100644
--- a/distage/distage-core-api/src/main/scala-2/izumi/distage/model/providers/FunctoidMacroMethods.scala
+++ b/distage/distage-core-api/src/main/scala-2/izumi/distage/model/providers/FunctoidMacroMethods.scala
@@ -28,5 +28,4 @@ trait FunctoidMacroMethods {
   implicit def apply[R](fun: (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) => R): Functoid[R] = macro FunctoidMacro.impl[R]
   implicit def apply[R](fun: (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) => R): Functoid[R] = macro FunctoidMacro.impl[R]
   implicit def apply[R](fun: (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) => R): Functoid[R] = macro FunctoidMacro.impl[R]
-
 }
diff --git a/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/DIUniverseLiftables.scala b/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/DIUniverseLiftables.scala
index ce93336e8..e4eeeb8b0 100644
--- a/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/DIUniverseLiftables.scala
+++ b/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/DIUniverseLiftables.scala
@@ -53,6 +53,7 @@ class DIUniverseLiftables[D <: StaticDIUniverse](val u: D) {
 
   // Associations
 
+  // converts Association.Parameter to LinkedParameter
   implicit val liftableParameter: Liftable[Association.Parameter] = {
     case Association.Parameter(symbol, key) =>
       q"new $modelReflectionPkg.LinkedParameter($symbol, $key)"
diff --git a/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/FunctoidMacro.scala b/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/FunctoidMacro.scala
index d81499322..d33d49554 100644
--- a/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/FunctoidMacro.scala
+++ b/distage/distage-core-api/src/main/scala-2/izumi/distage/model/reflection/macros/FunctoidMacro.scala
@@ -115,8 +115,8 @@ class FunctoidMacro(val c: blackbox.Context) {
 
     val lambdaParams = lambdaArgs.map(association)
     val methodReferenceParams = body match {
-      case Apply(f, _) =>
-        logger.log(s"Matched function body as a method reference - consists of a single call to a function $f - ${showRaw(body)}")
+      case Apply(f, args) if args.map(_.symbol) == lambdaArgs =>
+        logger.log(s"Matched function body as a method reference - consists of a single call to a function $f with the same arguments as lambda- ${showRaw(body)}")
 
         extractMethodReferenceParams(f.symbol).map(association)
       case _ =>
@@ -128,21 +128,29 @@ class FunctoidMacro(val c: blackbox.Context) {
     logger.log(s"lambda params: $lambdaParams")
     logger.log(s"method ref params: $methodReferenceParams")
 
-    def annotationsOnMethodAreNonEmptyAndASuperset: Boolean = {
-      val annotationsOnLambda = lambdaParams.iterator.map(_.symbol.annotations)
-      val annotationsOnMethod = Predef.wrapRefArray(methodReferenceParams.iterator.map(_.symbol.annotations).toArray)
-
-      annotationsOnMethod.exists(_.nonEmpty) &&
-      annotationsOnLambda.zipAll(annotationsOnMethod.iterator, null, null).forall {
-        case (null, _) => false
-        case (_, null) => false
-        case (left, right) =>
-          left.iterator.zipAll(right.iterator, null, null).forall {
-            case (l, r) => (l eq null) || l == r
-          }
-      }
+    @nowarn("msg=Unused import")
+    val annotationsOnMethodAreNonEmptyAndASuperset: Boolean = {
+      import scala.collection.compat._
+      methodReferenceParams.sizeCompare(lambdaParams) == 0 &&
+      methodReferenceParams.exists(_.symbol.annotations.nonEmpty)
     }
 
+//    // this is somewhat superfluous since normally lambda parameters can't be annotated in source code at all
+//    val annotationsOnMethodAreNonEmptyAndASuperset: Boolean = {
+//      val annotationsOnLambdaParamSymbols = lambdaParams.iterator.map(_.symbol.annotations)
+//      val annotationsOnMethod = Predef.wrapRefArray(methodReferenceParams.iterator.map(_.symbol.annotations).toArray)
+//
+//      annotationsOnMethod.exists(_.nonEmpty) &&
+//      annotationsOnLambdaParamSymbols.zipAll(annotationsOnMethod.iterator, null, null).forall {
+//        case (null, _) => false
+//        case (_, null) => false
+//        case (left, right) =>
+//          left.iterator.zipAll(right.iterator, null, null).forall {
+//            case (l, r) => (l eq null) || l == r
+//          }
+//      }
+//    }
+
     // if method reference has more annotations, get parameters from reference instead
     // to preserve annotations!
     if (annotationsOnMethodAreNonEmptyAndASuperset) {
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/AnyConstructorMacro.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/AnyConstructorMacro.scala
index 138fceda0..8c02c2528 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/AnyConstructorMacro.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/AnyConstructorMacro.scala
@@ -21,16 +21,19 @@ object AnyConstructorMacro {
     val tpe0 = TypeRepr.of[T].dealias.simplified
     val typeSymbol = tpe0.typeSymbol
 
+    // FIXME remove redundant check across macros
+    val util = new ConstructorUtil[qctx.type]()
+
     if (AndTypeTypeTest.unapply(tpe0).isDefined || RefinementTypeTest.unapply(tpe0).isDefined) {
       // ignore intersections for now
-      '{ ??? : AnyConstructor[T] }
-    } else if (tpe0.classSymbol.isDefined && !typeSymbol.flags.is(Flags.Trait) && !typeSymbol.flags.is(Flags.Abstract)) {
+      '{ (throw new RuntimeException("unsupported intersection")): AnyConstructor[T] }
+    } else if ((tpe0.classSymbol.isDefined && !typeSymbol.flags.is(Flags.Trait) && !typeSymbol.flags.is(Flags.Abstract)) || {
+        util.dropTypeRef(tpe0) match { case _: ConstantType | _: TermRef => true; case _ => false }
+      }) {
       ClassConstructorMacro.make[T]
     } else if ({
       // FIXME remove redundant check across macros
-      val util = new ConstructorUtil[qctx.type]()
-
-      val context = new ConstructorContext[T, qctx.type](util)
+      val context = new ConstructorContext[T, qctx.type, util.type](util)
       // TODO: check for sealed
       context.isWireableTrait
     }) {
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ClassConstructorMacro.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ClassConstructorMacro.scala
index 3d3fa7e68..b33d8493c 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ClassConstructorMacro.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ClassConstructorMacro.scala
@@ -1,9 +1,11 @@
 package izumi.distage.constructors
 
 import izumi.distage.model.providers.{Functoid, FunctoidMacro}
+import izumi.distage.model.reflection.Provider.ProviderType
 
 import scala.quoted.{Expr, Quotes, Type}
 import izumi.fundamentals.platform.exceptions.IzThrowable.toRichThrowable
+
 import scala.collection.immutable.ArraySeq
 
 object ClassConstructorMacro {
@@ -11,19 +13,20 @@ object ClassConstructorMacro {
   def make[R: Type](using qctx: Quotes): Expr[ClassConstructor[R]] = try {
     import qctx.reflect.*
 
-    val functoidMacro = new FunctoidMacro.FunctoidMacroImpl[qctx.type]()
     val util = new ConstructorUtil[qctx.type]()
-    import util.ParamListExt
 
-    Expr.summon[ValueOf[R]] match {
-      case Some(valexpr) =>
-        '{ new ClassConstructor[R](Functoid.singleton(${ valexpr.asExprOf[scala.Singleton & R] })) }
+    val typeRepr = TypeRepr.of[R].dealias.simplified
+
+    util.dropTypeRef(typeRepr) match {
+      case c: ConstantType =>
+        singletonClassConstructor[R](Literal(c.constant))
+
+      case t: TermRef =>
+        singletonClassConstructor[R](Ident(t))
 
       case _ =>
         util.requireConcreteTypeConstructor[R]("ClassConstructor")
 
-        val typeRepr = TypeRepr.of[R].dealias.simplified
-
         if (typeRepr.typeSymbol.flags.is(Flags.Trait) || typeRepr.typeSymbol.flags.is(Flags.Abstract)) {
           report.errorAndAbort(
             s"Cannot create ClassConstructor for type ${Type.show[R]} - it's a trait or an abstract class, not a concrete class. It cannot be constructed with `new`"
@@ -31,12 +34,12 @@ object ClassConstructorMacro {
         }
 
         typeRepr.classSymbol match {
-          case Some(cs) =>
+          case Some(_) =>
             val ctorTreeParameterized = util.buildConstructorApplication(typeRepr)
-            val constructorParamLists = util.buildConstructorParameters(typeRepr)._2.map(_.toTrees)
-            val lamExpr = util.wrapApplicationIntoLambda[R](constructorParamLists, ctorTreeParameterized)
+            val paramss = util.buildConstructorParameters(typeRepr)
+            val lamExpr = util.wrapCtorApplicationIntoFunctoidRawLambda[R](paramss, ctorTreeParameterized)
 
-            val f = functoidMacro.make[R](lamExpr)
+            val f = util.makeFunctoid[R](paramss.flatten, lamExpr, '{ ProviderType.Class })
             '{ new ClassConstructor[R](${ f }) }
 
           case None =>
@@ -45,4 +48,12 @@ object ClassConstructorMacro {
     }
   } catch { case t: Throwable => qctx.reflect.report.errorAndAbort(t.stackTrace) }
 
+  private def singletonClassConstructor[R0](using qctx: Quotes, rtpe0: Type[R0])(tree: qctx.reflect.Tree): Expr[ClassConstructor[R0]] = {
+    type R <: R0 & Singleton
+    (rtpe0: @unchecked) match {
+      case given Type[R] =>
+        '{ new ClassConstructor[R](Functoid.singleton[R](${ tree.asExprOf[R] })) }
+    }
+  }
+
 }
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ConstructorUtil.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ConstructorUtil.scala
index 39196d363..45bc4e693 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ConstructorUtil.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/ConstructorUtil.scala
@@ -3,12 +3,14 @@ package izumi.distage.constructors
 import izumi.distage.model.definition.With
 import izumi.fundamentals.platform.reflection.ReflectionUtil
 
-import scala.annotation.tailrec
+import scala.annotation.{nowarn, tailrec}
 import scala.quoted.{Expr, Quotes, Type}
 import scala.collection.mutable
-import izumi.distage.model.providers.FunctoidMacro
+import izumi.distage.model.providers.{Functoid, FunctoidMacro}
+import izumi.distage.model.providers.FunctoidMacro.FunctoidParametersMacro
+import izumi.distage.model.reflection.Provider.{ProviderImpl, ProviderType}
 
-class ConstructorContext[R: Type, Q <: Quotes](using val qctx: Q)(val util: ConstructorUtil[qctx.type]) {
+class ConstructorContext[R: Type, Q <: Quotes, U <: ConstructorUtil[Q]](using val qctx: Q)(val util: U & ConstructorUtil[qctx.type]) {
   import qctx.reflect.*
 
   val resultTpe = TypeRepr.of[R].dealias.simplified
@@ -31,10 +33,10 @@ class ConstructorContext[R: Type, Q <: Quotes](using val qctx: Q)(val util: Cons
 
   val parentsSymbols = util.findRequiredImplParents(resultTpeSym)
   val parentTypesParameterized = parentsSymbols.map(resultTpe.baseType)
-  val constructorParamLists = parentTypesParameterized.map(util.buildConstructorParameters)
+  val constructorParamLists = parentTypesParameterized.map(t => t -> util.buildConstructorParameters(t))
   val flatCtorParams = constructorParamLists.flatMap(_._2.iterator.flatten)
 
-  val methodDecls = abstractMembers.map(m => (m.name, m.flags.is(Flags.Method), resultTpe.memberType(m))) ++ refinementMethods
+  val methodDecls = abstractMembers.map(m => (m.name, m.flags.is(Flags.Method), Some(m), resultTpe.memberType(m))) ++ refinementMethods
 
   def isFactory: Boolean = abstractMembers.nonEmpty || refinementMethods.nonEmpty
 
@@ -44,29 +46,24 @@ class ConstructorContext[R: Type, Q <: Quotes](using val qctx: Q)(val util: Cons
 class ConstructorUtil[Q <: Quotes](using val qctx: Q) {
   import qctx.reflect.*
 
-  private val withAnnotationSym = TypeRepr.of[With].typeSymbol
+  private val withAnnotationSym: Symbol = TypeRepr.of[With].typeSymbol
 
-  type ParamListRepr = List[(String, TypeRepr)]
-  type ParamListsRepr = List[ParamListRepr]
-  type ParamListTree = List[(String, TypeTree)]
-  type ParamListsTree = List[ParamListTree]
+  final case class ParamRepr(name: String, mbSymbol: Option[Symbol], tpe: TypeRepr)
 
-  implicit object ParamListExt {
-    extension (params: ParamListRepr) {
-      def toTrees: ParamListTree = params.map((n, t) => (n, TypeTree.of(using t.asType))).toList
-    }
-  }
+  type ParamReprList = List[ParamRepr]
+  type ParamReprLists = List[ParamReprList]
 
-  def assertSignatureIsAcceptableForFactory(signatureParams: ParamListRepr, resultTpe: TypeRepr, clue: String): Unit = {
-    assert(signatureParams.groupMap(_._1)(_._2).forall(_._2.size == 1), "BUG: duplicated arg names!")
+  def assertSignatureIsAcceptableForFactory(signatureParams: ParamReprList, resultTpe: TypeRepr, clue: String): Unit = {
+    assert(signatureParams.groupMap(_.name)(_.tpe).forall(_._2.size == 1), "BUG: duplicated arg names!")
 
-    val sigRevIndex = signatureParams.groupMap(_._2)(_._1)
+    val sigRevIndex = signatureParams.groupMap(_.tpe)(_.name)
     val duplications = sigRevIndex.filter(_._2.size > 1)
 
     if (duplications.nonEmpty) {
       import izumi.fundamentals.platform.strings.IzString.*
       val explanation = duplications.map((t, nn) => s"${t.show}: ${nn.mkString(", ")}").niceList()
-      report.errorAndAbort(s"Cannot build factory for ${resultTpe.show}, $clue contais contradicting arguments: $explanation")
+//      report.errorAndAbort(s"Cannot build factory for ${resultTpe.show}, $clue contais contradicting arguments: $explanation")
+      report.warning(s"Cannot build factory for ${resultTpe.show}, $clue contais contradicting arguments: $explanation")
     }
 
     sigRevIndex.view.mapValues(_.head).toMap
@@ -88,8 +85,72 @@ class ConstructorUtil[Q <: Quotes](using val qctx: Q) {
     }
   }
 
-  def wrapApplicationIntoLambda[R: Type](paramss: ParamListsTree, constructorTerm: Term): Expr[Any] = {
-    wrapIntoLambda[R](paramss) {
+  def makeFunctoid[R: Type](params: ParamReprList, argsLambda: Expr[Seq[Any] => R], providerType: Expr[ProviderType]): Expr[Functoid[R]] = {
+    val paramsMacro = new FunctoidParametersMacro[qctx.type]
+
+    val paramDefs = params.map {
+      case ParamRepr(n, s, t) => paramsMacro.makeParam(n, Right(t), s)
+    }
+
+    val out = '{
+      new Functoid[R](
+        new ProviderImpl[R](
+          ${ Expr.ofList(paramDefs) },
+          ${ paramsMacro.safeType[R] },
+          ${ argsLambda },
+          ${ providerType },
+        )
+      )
+    }
+
+    report.warning(
+      s"""ConstructorUtil:fun=${argsLambda.show}
+         |funType=${argsLambda.asTerm.tpe}
+         |funSym=${argsLambda.asTerm.symbol}
+         |funTypeSym=${argsLambda.asTerm.tpe.typeSymbol}
+         |funTypeSymBases=${argsLambda.asTerm.tpe.baseClasses}
+         |params=${params.map(p => s"$p:symbol-annos(${p.mbSymbol.map(s => s -> s.annotations)})")}
+         |outputType=${Type.show[R]}
+         |rawOutputType=(${TypeRepr.of[R]})
+         |providerType=${providerType.show}
+         |produced=${out.show}""".stripMargin
+    )
+
+    out
+  }
+
+  def wrapIntoFunctoidRawLambda[R: Type](
+    params: ParamReprList
+  )(body: (Symbol, List[Term]) => Tree
+  ): Expr[Seq[Any] => R] = {
+    val mtpe = MethodType(List("args"))(
+      _ => List(TypeRepr.of[Seq[Any]]),
+      _ => TypeRepr.of[R],
+    )
+    Lambda(
+      Symbol.spliceOwner,
+      mtpe,
+      {
+        case (lamSym, (args: Term) :: Nil) =>
+          val argRefs = params.iterator.zipWithIndex.map {
+            case (ParamRepr(_, _, paramTpe), idx) =>
+              paramTpe match {
+                case ByNameUnwrappedAsType('[t]) =>
+                  '{ ${ args.asExprOf[Seq[Any]] }.apply(${ Expr(idx) }).asInstanceOf[() => t].apply() }.asTerm
+                case AsType('[t]) =>
+                  '{ ${ args.asExprOf[Seq[Any]] }.apply(${ Expr(idx) }).asInstanceOf[t] }.asTerm
+                case _ =>
+                  report.errorAndAbort(s"Invalid higher-kinded type $paramTpe ${paramTpe.show}")
+              }
+          }.toList
+
+          body(lamSym, argRefs)
+      }: @nowarn("msg=match"),
+    ).asExprOf[Seq[Any] => R]
+  }
+
+  def wrapCtorApplicationIntoFunctoidRawLambda[R: Type](paramss: ParamReprLists, constructorTerm: Term): Expr[Seq[Any] => R] = {
+    wrapIntoFunctoidRawLambda[R](paramss.flatten) {
       (_, args0) =>
         import scala.collection.immutable.Queue
         val (_, argsLists) = paramss.foldLeft((args0, Queue.empty[List[Term]])) {
@@ -99,58 +160,69 @@ class ConstructorUtil[Q <: Quotes](using val qctx: Q) {
         }
 
         val appl = argsLists.foldLeft(constructorTerm)(_.appliedToArgs(_))
-        val trm = Typed(appl, TypeTree.of[R])
-        trm
+        Typed(appl, TypeTree.of[R])
     }
   }
 
-  def wrapIntoLambda[R: Type](
-    paramss: ParamListsTree
-  )(body: (Symbol, List[Term]) => Tree
-  ): Expr[Any] = {
-    val params = paramss.flatten
-    val mtpe = MethodType(params.map(_._1))(
-      _ => params.map(t => t._2.tpe),
-      _ => TypeRepr.of[R],
-    )
-    val lam = Lambda(
-      Symbol.spliceOwner,
-      mtpe,
-      {
-        case (lamSym, args0) =>
-          body(lamSym, args0.asInstanceOf[List[Term]])
-      },
-    )
-    lam.asExpr
+  private object ByNameUnwrappedAsType {
+    def unapply(t: TypeRepr): Option[Type[?]] = {
+      t match {
+        case ByNameType(u) => Some(u.asType)
+        case _ => None
+      }
+    }
   }
 
-  def unpackRefinement(t: TypeRepr): List[(String, Boolean, MethodType)] = {
-    t match {
-      case Refinement(parent, name, m: MethodType) =>
-        List((name, true, m)) ++ unpackRefinement(parent)
-      case _ =>
-        List.empty
+  private object AsType {
+    def unapply(t: TypeRepr): Some[Type[?]] = {
+      Some(t.asType)
     }
   }
 
-  def flattenMethodSignature(t: TypeRepr): ParamListRepr = {
+  def unpackRefinement(t: TypeRepr): List[(String, Boolean, Option[Symbol], MethodOrPoly)] = {
     t match {
-      case MethodType(nn, tt, ret) =>
-        nn.zip(tt) ++ flattenMethodSignature(ret)
+      case Refinement(parent, name, m: MethodOrPoly) =>
+        (name, true, None, m) :: unpackRefinement(parent)
       case _ =>
-        List.empty
+        Nil
     }
   }
 
+  def extractMethodSignature(t0: TypeRepr, methodSym: Symbol): ParamReprLists = {
+    val paramSymssExcTypes = methodSym.paramSymss.filterNot(_.headOption.exists(_.isTypeParam))
+
+    def go(t: TypeRepr, paramSymss: List[List[Symbol]]): ParamReprLists = {
+      t match {
+        case mtpe @ MethodType(nn, tt, ret) =>
+          nn.iterator
+            .zip(tt)
+            .zipAll(paramSymss match { case h :: _ => h; case _ => List.empty[Symbol] }, null, null.asInstanceOf[Symbol])
+            .map {
+              case (null, _) => null
+              case ((n, t), maybeSymbol) => ParamRepr(n, Option(maybeSymbol), t)
+            }.takeWhile(_ ne null).toList :: go(ret, paramSymss.drop(1))
+        case PolyType(_, _, ret) =>
+          go(ret, paramSymss)
+        case _ =>
+          List.empty
+      }
+    }
+
+    go(t0, paramSymssExcTypes)
+  }
+
   @tailrec
-  final def realReturnType(t: TypeRepr): TypeRepr = {
+  final def returnTypeOfMethod(t: TypeRepr): TypeRepr = {
     t match {
       case MethodType(_, _, ret) =>
-        realReturnType(ret)
+        returnTypeOfMethod(ret)
+      case PolyType(_, _, ret) =>
+        returnTypeOfMethod(ret)
       case r =>
         r
     }
   }
+
   final def ensureByName(tpe: TypeRepr): TypeRepr = {
     tpe match {
       case t @ ByNameType(_) => t
@@ -158,39 +230,33 @@ class ConstructorUtil[Q <: Quotes](using val qctx: Q) {
     }
   }
 
-  @tailrec final def dropMethodType(tpe: TypeRepr): TypeRepr = {
+  @tailrec final def returnTypeOfMethodOrByName(tpe: TypeRepr): TypeRepr = {
     tpe match {
+      case ByNameType(t) =>
+        returnTypeOfMethodOrByName(t)
       case MethodType(_, _, t) =>
-        dropMethodType(t)
+        returnTypeOfMethodOrByName(t)
+      case PolyType(_, _, t) =>
+        returnTypeOfMethodOrByName(t)
       case t =>
         t
     }
   }
 
-//  final def dropAnno(tpe: TypeRepr): TypeRepr = {
-//    tpe match {
-//      case AnnotatedType(t, _) =>
-//        t
-//      case t => t
-//    }
-//  }
-
-  @tailrec final def dropWrappers(tpe: TypeRepr): TypeRepr = {
+  def dropTypeRef(tpe: TypeRepr): TypeRepr = {
     tpe match {
-      case ByNameType(t) =>
-        dropWrappers(t)
-      case MethodType(_, _, t) =>
-        dropWrappers(t)
-      case t =>
-        t
+      case t: TypeRef =>
+        t.typeSymbol.owner.typeRef.memberType(t.typeSymbol)
+      case _ =>
+        tpe
     }
   }
 
-  def readWithAnno(tpe: TypeRepr): Option[TypeRepr] = {
+  def readWithAnnotation(tpe: TypeRepr): Option[TypeRepr] = {
     tpe match {
-      case AnnotatedType(_, aterm) =>
+      case AnnotatedType(_, aterm) if aterm.tpe.baseClasses.contains(withAnnotationSym) =>
         aterm match {
-          case Apply(TypeApply(Select(New(_), _), c :: _), _) if aterm.tpe.baseClasses.contains(withAnnotationSym) =>
+          case Apply(TypeApply(Select(New(_), _), c :: _), _) =>
             Some(c.tpe)
           case _ =>
             report.errorAndAbort(s"distage.With annotation expects one type argument but got malformed tree ${aterm.show} ($aterm) : ${aterm.tpe}")
@@ -247,31 +313,23 @@ class ConstructorUtil[Q <: Quotes](using val qctx: Q) {
     }
   }
 
-  def buildConstructorParameters(tpe: TypeRepr): (TypeRepr, ParamListsRepr) = {
-    val methodTypeApplied = tpe.memberType(tpe.typeSymbol.primaryConstructor).appliedTo(tpe.typeArgs)
-
-    val paramListsRepr: ParamListsRepr = {
-      def go(t: TypeRepr): ParamListsRepr = {
-        t match {
-          case MethodType(paramNames, paramTpes, res) =>
-            paramNames.zip(paramTpes) :: go(res)
-          case _ =>
-            Nil
-        }
+  def buildConstructorParameters(tpe: TypeRepr): ParamReprLists = {
+    val ctorMethodTypeApplied =
+      try {
+        tpe.memberType(tpe.typeSymbol.primaryConstructor).appliedTo(tpe.typeArgs)
+      } catch {
+        case t: Throwable =>
+          throw new RuntimeException(s"Got $t in tpe=${tpe.show} prim=${tpe.typeSymbol.primaryConstructor}, pt=${tpe.typeSymbol.primaryConstructor.typeRef}")
       }
 
-      go(methodTypeApplied)
-    }
-
-    tpe -> paramListsRepr
+    extractMethodSignature(ctorMethodTypeApplied, tpe.typeSymbol.primaryConstructor)
   }
 
   def buildConstructorApplication(baseType: TypeRepr): Term = {
     Some(baseType.typeSymbol.primaryConstructor).filterNot(_.isNoSymbol) match {
       case Some(consSym) =>
         val ctorTree = Select(New(TypeTree.of(using baseType.asType)), consSym)
-        val ctorTreeParameterized = ctorTree.appliedToTypeTrees(baseType.typeArgs.map(repr => TypeTree.of(using repr.asType)))
-        ctorTreeParameterized
+        ctorTree.appliedToTypeTrees(baseType.typeArgs.map(repr => TypeTree.of(using repr.asType)))
       case None =>
         report.errorAndAbort(s"Cannot find primary constructor in $baseType")
     }
@@ -279,7 +337,7 @@ class ConstructorUtil[Q <: Quotes](using val qctx: Q) {
 
   def buildParentConstructorCallTerms(
     resultTpe: TypeRepr,
-    constructorParamListsRepr: List[(TypeRepr, ParamListsRepr)],
+    constructorParamListsRepr: List[(TypeRepr, ParamReprLists)],
     contextParameters: List[Term],
   ): Seq[Term] = {
     import scala.collection.immutable.Queue
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/FactoryConstructorMacro.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/FactoryConstructorMacro.scala
index 7750d5b8e..59b9be9de 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/FactoryConstructorMacro.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/FactoryConstructorMacro.scala
@@ -1,28 +1,25 @@
 package izumi.distage.constructors
 
 import izumi.distage.model.providers.{Functoid, FunctoidMacro}
+import izumi.distage.model.reflection.Provider.ProviderType
 import izumi.fundamentals.platform.exceptions.IzThrowable.toRichThrowable
 
 import scala.annotation.experimental
 import scala.collection.immutable.{ArraySeq, Queue}
 import scala.collection.mutable
 import scala.quoted.{Expr, Quotes, Type}
+import scala.util.control.NonFatal
 
 object FactoryConstructorMacro {
 
-  def make[R: Type](using qctx: Quotes): Expr[FactoryConstructor[R]] = '{ ??? }
-
   @experimental
-  def make0[R: Type](using qctx: Quotes): Expr[FactoryConstructor[R]] = try {
+  def make[R: Type](using qctx: Quotes): Expr[FactoryConstructor[R]] = try {
     import qctx.reflect.*
 
-    val functoidMacro = new FunctoidMacro.FunctoidMacroImpl[qctx.type]()
-
     val util = new ConstructorUtil[qctx.type]()
-    import util.ParamListExt
-    import util.{ParamListRepr, ParamListsRepr}
+    import util.{ParamRepr, ParamReprList, ParamReprLists}
 
-    val context = new ConstructorContext[R, qctx.type](util)
+    val context = new ConstructorContext[R, qctx.type, util.type](util)
     import context.*
 
     if (!isFactory) {
@@ -31,88 +28,94 @@ object FactoryConstructorMacro {
       )
     }
 
-    val refinementNames = refinementMethods.map(_._1).toSet
+    val refinementNames = refinementMethods.iterator.map(_._1).toSet
 
     def decls(cls: Symbol): List[Symbol] = methodDecls.map {
-      case (name, _, mtype) =>
+      case (name, _, _, mtype) =>
         // for () methods MethodType(Nil)(_ => Nil, _ => m.returnTpt.symbol.typeRef) instead of mtype
         val overrideFlag = if (!refinementNames.contains(name)) Flags.Override else Flags.EmptyFlags
         Symbol.newMethod(cls, name, mtype, overrideFlag | Flags.Method, Symbol.noSymbol)
     }
 
     sealed trait Parameter
-    case class ProvidedParameter(constructorName: String, argName: String, tpe: TypeRepr, flatLamdaSigIndex: Int) extends Parameter
-    case class SignatureParameter(sigName: String, tpe: TypeRepr, flatLocalSigIndex: Int) extends Parameter
+    final case class DependencyParameter(paramName: String, mbParamSymbol: Option[Symbol], paramTpe: TypeRepr, argName: String, flatLambdaSigIndex: Int) extends Parameter
+    final case class MethodParameter(sigName: String, tpe: TypeRepr, flatLocalSigIndex: Int) extends Parameter
 
-    case class FactoryMethodDecl(
+    final case class FactoryMethodDecl(
       name: String,
       impl: TypeRepr,
-      implSymb: Symbol,
+      implTypeSym: Symbol,
       params: List[List[Parameter]],
     )
 
-    var flatLamdaSigIndex = 0
+    var flatLambdaSigIndex = 0
     val factoryMethodData = methodDecls.map {
-      (n, _, t) =>
-        val signatureParams = util.flattenMethodSignature(t)
-        val rett = util.dropWrappers(util.realReturnType(t))
-        val impltype = util.readWithAnno(rett).getOrElse(rett).dealias.simplified
-
-        if (impltype.typeSymbol.flags.is(Flags.Trait) || impltype.typeSymbol.flags.is(Flags.Abstract)) {
-          report.errorAndAbort(
-            s"Cannot build factory for ${resultTpe.show}, factory method $n returns type ${impltype.show} which cannot be constructed with `new`"
-          )
+      (n, _, mbSym, t) =>
+        val signatureParams = util.extractMethodSignature(t, mbSym.getOrElse(Symbol.noSymbol)).flatten
+        val rett = util.returnTypeOfMethodOrByName(t)
+        val factoryProductType = util.readWithAnnotation(rett).getOrElse(rett).dealias.simplified
+
+        val isTrait = factoryProductType.typeSymbol.flags.is(Flags.Trait) || factoryProductType.typeSymbol.flags.is(Flags.Abstract)
+        if (isTrait) {
+//          report.errorAndAbort(
+//            s"Cannot build factory for ${resultTpe.show}, factory method $n returns type ${impltype.show} which cannot be constructed with `new`"
+//          )
+          val msg = s"Cannot build factory for ${resultTpe.show}, factory method $n returns type ${factoryProductType.show} which cannot be constructed with `new`"
+          return '{ (throw new RuntimeException(${ Expr(msg) })): FactoryConstructor[R] }
         }
 
-        val constructorParamLists = util.buildConstructorParameters(impltype)
+        val constructorParamLists = util.buildConstructorParameters(factoryProductType)
 
         util.assertSignatureIsAcceptableForFactory(signatureParams, resultTpe, s"factory method $n")
-        util.assertSignatureIsAcceptableForFactory(constructorParamLists._2.flatten, resultTpe, s"implementation constructor ${impltype.show}")
+        util.assertSignatureIsAcceptableForFactory(constructorParamLists.flatten, resultTpe, s"implementation constructor ${factoryProductType.show}")
 
         val indexedSigParams = signatureParams.zipWithIndex
-        val sigRevIndex = indexedSigParams.map { case ((n, t), i) => (t, (n, i)) }.toMap
+        val sigRevIndex = indexedSigParams.map { case (ParamRepr(n, _, t), idx) => (t, (n, idx)) }.toMap
 
-        val params = constructorParamLists._2.zipWithIndex.map {
+        val params = constructorParamLists.zipWithIndex.map {
           case (pl, listIdx) =>
             pl.map {
-              case (pn, pt) =>
-                sigRevIndex.find((t, _) => util.dropWrappers(t) =:= util.dropWrappers(pt)) match {
-                  case Some((_, (_, i))) =>
-                    SignatureParameter(pn, pt, i)
+              case ParamRepr(pn, s, pt) =>
+                sigRevIndex.find((t, _) => util.returnTypeOfMethodOrByName(t) =:= util.returnTypeOfMethodOrByName(pt)) match {
+                  case Some((_, (_, idx))) =>
+                    MethodParameter(pn, pt, idx)
 
                   case None =>
-                    val curIndex = flatLamdaSigIndex
-                    flatLamdaSigIndex += 1
+                    val curIndex = flatLambdaSigIndex
+                    flatLambdaSigIndex += 1
                     val newName = if (listIdx > 0) {
                       s"_${n}_${listIdx}_$pn"
                     } else {
                       s"_${n}_$pn"
                     }
-                    ProvidedParameter(pn, newName, util.ensureByName(pt), curIndex)
+                    DependencyParameter(pn, s, util.ensureByName(pt), newName, curIndex)
                 }
             }
         }
 
-        val consumedSigParams = params.flatten.collect { case p: SignatureParameter => p.flatLocalSigIndex }.toSet
+        val consumedSigParams = params.flatten.collect { case p: MethodParameter => p.flatLocalSigIndex }.toSet
         val unconsumedParameters = indexedSigParams.filterNot(p => consumedSigParams.contains(p._2))
 
         if (unconsumedParameters.nonEmpty) {
           import izumi.fundamentals.platform.strings.IzString.*
-          val explanation = unconsumedParameters.map { case ((n, t), _) => s"$n: ${t.show}" }.niceList()
-          report.errorAndAbort(
-            s"Cannot build factory for ${resultTpe.show}, factory method $n has arguments which were not consumed by implementation constructor ${impltype.show}: $explanation"
-          )
+          val explanation = unconsumedParameters.map { case (ParamRepr(n, _, t), _) => s"$n: ${t.show}" }.niceList()
+//          report.errorAndAbort(
+//            s"Cannot build factory for ${resultTpe.show}, factory method $n has arguments which were not consumed by implementation constructor ${impltype.show}: $explanation"
+//          )
+          val msg =
+            s"Cannot build factory for ${resultTpe.show}, factory method $n has arguments which were not consumed by implementation constructor ${factoryProductType.show}: $explanation"
+          return '{ (throw new RuntimeException(${ Expr(msg) })): FactoryConstructor[R] }
         }
 
-        FactoryMethodDecl(n, impltype, impltype.typeSymbol, params)
+        FactoryMethodDecl(n, factoryProductType, factoryProductType.typeSymbol, params)
     }
 
-    val ctorArgs = flatCtorParams.map((n, t) => (n, util.dropMethodType(t)))
-    val byNameMethodArgs = factoryMethodData.flatMap(_.params).flatten.collect { case p: ProvidedParameter => (p.argName, p.tpe) }
-    val lamParams: util.ParamListTree = (ctorArgs ++ byNameMethodArgs).toTrees
+    val ctorArgs = flatCtorParams.map { case ParamRepr(n, s, t) => ParamRepr(n, s, util.returnTypeOfMethodOrByName(t)) }
+    val byNameMethodArgs = factoryMethodData.flatMap(_.params).flatten.collect { case p: DependencyParameter => ParamRepr(p.argName, p.mbParamSymbol, p.paramTpe) }
+    val lamParams: ParamReprList = ctorArgs ++ byNameMethodArgs
     val indexShift = ctorArgs.length
 
-    val lamExpr = util.wrapIntoLambda[R](List(lamParams)) {
+    val lamExpr = util.wrapIntoFunctoidRawLambda[R](lamParams) {
       (lamSym, args0) =>
 
         val (lamOnlyCtorArguments, _) = args0.splitAt(flatCtorParams.size)
@@ -120,14 +123,22 @@ object FactoryConstructorMacro {
         val parents = util.buildParentConstructorCallTerms(resultTpe, constructorParamLists, lamOnlyCtorArguments)
 
         val name: String = s"${resultTpeSym.name}FactoryAutoImpl"
-        val clsSym = Symbol.newClass(lamSym, name, parents = parentTypesParameterized, decls = decls, selfType = None)
-
-        val defs = factoryMethodData.map {
-          fmd =>
-            val methodSyms = clsSym.declaredMethod(fmd.name)
-            assert(methodSyms.size == 1, "BUG: duplicated methods!")
-            val methodSym = methodSyms.head
-
+        var declSymbols: List[Symbol] = null
+        val clsSym = Symbol.newClass(
+          lamSym,
+          name,
+          parents = parentTypesParameterized,
+          decls = {
+            s =>
+              val methods = decls(s)
+              declSymbols = methods
+              methods
+          },
+          selfType = None,
+        )
+
+        val defs = factoryMethodData.zip(declSymbols).map {
+          (fmd, methodSym) =>
             DefDef(
               methodSym,
               sigArgs => {
@@ -138,10 +149,16 @@ object FactoryConstructorMacro {
                 val argsLists: List[List[Term]] = fmd.params.map {
                   pl =>
                     pl.map {
-                      case p: ProvidedParameter =>
-                        args0(p.flatLamdaSigIndex + indexShift)
-                      case p: SignatureParameter =>
-                        sigFlat(p.flatLocalSigIndex).asExpr.asTerm
+                      case p: DependencyParameter =>
+                        args0(p.flatLambdaSigIndex + indexShift)
+                      case p: MethodParameter =>
+                        sigFlat(p.flatLocalSigIndex) match {
+                          case t: Term => t
+                          case o =>
+                            // FIXME support type parameters (GenericAssistedFactory)
+                            return '{ (throw new RuntimeException("FIXME: support type parameters")): FactoryConstructor[R] }
+//                            report.errorAndAbort(s"Impossible, not defdef argument not a term = $o (${o.show}) in $sigArgs (sym sig: ${methodSym.signature})")
+                        }
                     }
                 }
 
@@ -178,9 +195,9 @@ object FactoryConstructorMacro {
 //          |""".stripMargin
 //    )
 
-    val f = functoidMacro.make[R](lamExpr)
+    val f = util.makeFunctoid[R](lamParams, lamExpr, '{ ProviderType.Factory })
     '{ new FactoryConstructor[R](${ f }) }
 
-  } catch { case t: Throwable => qctx.reflect.report.errorAndAbort(t.stackTrace) }
+  } catch { case NonFatal(t) => qctx.reflect.report.errorAndAbort(t.stackTrace) }
 
 }
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/TraitConstructorMacro.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/TraitConstructorMacro.scala
index 780d0deb2..ce4384101 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/TraitConstructorMacro.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/TraitConstructorMacro.scala
@@ -1,6 +1,7 @@
 package izumi.distage.constructors
 
 import izumi.distage.model.providers.{Functoid, FunctoidMacro}
+import izumi.distage.model.reflection.Provider.ProviderType
 import izumi.fundamentals.platform.exceptions.IzThrowable.toRichThrowable
 
 import scala.annotation.experimental
@@ -14,12 +15,10 @@ object TraitConstructorMacro {
   def make[R: Type](using qctx: Quotes): Expr[TraitConstructor[R]] = try {
     import qctx.reflect.*
 
-    val functoidMacro = new FunctoidMacro.FunctoidMacroImpl[qctx.type]()
-
     val util = new ConstructorUtil[qctx.type]()
-    import util.ParamListExt
+    import util.ParamRepr
 
-    val context = new ConstructorContext[R, qctx.type](util)
+    val context = new ConstructorContext[R, qctx.type, util.type](util)
     import context.*
 
     if (!context.isWireableTrait) {
@@ -33,7 +32,7 @@ object TraitConstructorMacro {
     assert(methodDecls.map(_._1).size == methodDecls.size, "BUG: duplicated abstract method names")
 
     def decls(cls: Symbol): List[Symbol] = methodDecls.map {
-      case (name, isMethod, mtype) =>
+      case (name, isMethod, _, mtype) =>
         // for () methods MethodType(Nil)(_ => Nil, _ => m.returnTpt.symbol.typeRef) instead of mtype
         if (isMethod) {
           Symbol.newMethod(cls, name, mtype, Flags.Method | Flags.Override, Symbol.noSymbol)
@@ -44,12 +43,12 @@ object TraitConstructorMacro {
 
     // TODO: decopypaste
     val lamParams = {
-      val ctorArgs = flatCtorParams.map((n, t) => (n, util.dropMethodType(t)))
-      val byNameMethodArgs = methodDecls.map((n, _, t) => (s"_$n", util.ensureByName(util.dropWrappers(t))))
-      (ctorArgs ++ byNameMethodArgs).toTrees
+      val ctorArgs = flatCtorParams.map { case ParamRepr(n, s, t) => ParamRepr(n, s, util.returnTypeOfMethod(t)) }
+      val byNameMethodArgs = methodDecls.map { case (n, _, s, t) => ParamRepr(s"_$n", s, util.ensureByName(util.returnTypeOfMethodOrByName(t))) }
+      ctorArgs ++ byNameMethodArgs
     }
 
-    val lamExpr = util.wrapIntoLambda[R](List(lamParams)) {
+    val lamExpr = util.wrapIntoFunctoidRawLambda[R](lamParams) {
       (lamSym, args0) =>
 
         val (lamOnlyCtorArguments, lamOnlyMethodArguments) = args0.splitAt(flatCtorParams.size)
@@ -60,7 +59,7 @@ object TraitConstructorMacro {
         val clsSym = Symbol.newClass(lamSym, name, parents = parentTypesParameterized, decls = decls, selfType = None)
 
         val defs = methodDecls.zip(lamOnlyMethodArguments).map {
-          case ((name, isMethod, tpe), arg) =>
+          case ((name, isMethod, _, _), arg) =>
             val methodSyms = if (isMethod) clsSym.declaredMethod(name) else List(clsSym.declaredField(name))
             assert(methodSyms.size == 1, "BUG: duplicated methods!")
             val methodSym = methodSyms.head
@@ -97,7 +96,7 @@ object TraitConstructorMacro {
           |""".stripMargin
     )
 
-    val f = functoidMacro.make[R](lamExpr)
+    val f = util.makeFunctoid[R](lamParams, lamExpr, '{ ProviderType.Trait })
     '{ new TraitConstructor[R](${ f }) }
 
   } catch { case t: Throwable => qctx.reflect.report.errorAndAbort(t.stackTrace) }
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/constructors.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/constructors.scala
index 901cde4d7..4994457e0 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/constructors.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/constructors/constructors.scala
@@ -16,8 +16,10 @@ import izumi.fundamentals.platform.reflection.ReflectionUtil
   *
   *   - concrete classes (using [[ClassConstructor]])
   *   - traits and abstract classes ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]], using [[TraitConstructor]])
-  *   - "factory-like" traits and abstract classes ([[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]], using [[FactoryConstructor]])
-  *   - `zio.Has` heterogeneous map values for use by ZIO or other Reader-like effects ([[https://izumi.7mind.io/distage/basics.html#zio-has-bindings ZIO Has bindings]], using [[HasConstructor]])
+  *
+  * Since version `1.1.0`, does not generate constructors "factory-like" traits and abstract classes, instead use [[FactoryConstructor]].
+  *
+  * Use [[HasConstructor]] to generate constructors for `zio.Has` values.
   *
   * @example
   * {{{
diff --git a/distage/distage-core-api/src/main/scala-3/izumi/distage/model/providers/FunctoidMacroMethods.scala b/distage/distage-core-api/src/main/scala-3/izumi/distage/model/providers/FunctoidMacroMethods.scala
index 530ca9aea..ba3cf9323 100644
--- a/distage/distage-core-api/src/main/scala-3/izumi/distage/model/providers/FunctoidMacroMethods.scala
+++ b/distage/distage-core-api/src/main/scala-3/izumi/distage/model/providers/FunctoidMacroMethods.scala
@@ -2,7 +2,6 @@ package izumi.distage.model.providers
 
 import izumi.distage.model.reflection.Provider.{ProviderImpl, ProviderType}
 import izumi.distage.model.reflection.*
-import izumi.distage.constructors.ClassConstructorMacro
 import izumi.reflect.Tag
 import izumi.distage.model.definition.Id
 
@@ -64,99 +63,9 @@ trait FunctoidMacroMethods {
 }
 
 object FunctoidMacro {
-  inline def make[R](inline fun: Any): Functoid[R] = ${ makeImpl[R]('fun) }
+  inline def make[R](inline fun: AnyRef): Functoid[R] = ${ makeImpl[R]('fun) }
 
-  def makeImpl[R: Type](fun: Expr[Any])(using qctx: Quotes): Expr[Functoid[R]] = new FunctoidMacroImpl[qctx.type]().make(fun)
-
-  final class FunctoidParametersMacro[Q <: Quotes](using val qctx: Q) {
-    import qctx.reflect.*
-
-    val idAnnotationSym = TypeRepr.of[Id].typeSymbol
-
-    def makeParams[R: Type](params: List[(String, TypeTree)]): (List[Expr[LinkedParameter]], List[TypeTree]) = {
-      val paramTypes = params.map(_._2)
-
-      val paramDefs = params.map {
-        case (name, tpe) =>
-          val identifier = tpe match {
-            case Annotated(_, aterm) if aterm.tpe.baseClasses.contains(idAnnotationSym) =>
-              aterm match {
-                case Apply(Select(New(_), _), c :: _) =>
-                  c.asExprOf[String].value.orElse {
-                    report.errorAndAbort(s"distage.Id annotation expects one literal argument but got ${c.show} in tree ${aterm.show} ($aterm)")
-                  }
-                case _ =>
-                  report.errorAndAbort(s"distage.Id annotation expects one literal argument but got malformed tree ${aterm.show} ($aterm)")
-              }
-            case _ =>
-              None
-          }
-
-          val isByName = tpe.tpe match {
-            case ByNameType(_) => true
-            case _ => false
-          }
-
-          '{
-            LinkedParameter(
-              SymbolInfo(
-                name = ${
-                  Expr(name)
-                },
-                finalResultType = ${ safeType(tpe) },
-                isByName = ${ Expr(isByName) },
-                wasGeneric = ${ Expr(tpe.tpe.typeSymbol.isTypeParam) }, // TODO: type members?
-              ),
-              ${ makeKey(tpe, identifier) },
-            )
-          }
-      }
-
-      (paramDefs, paramTypes)
-    }
-
-    def makeKey(tpe: TypeTree, id: Option[String]): Expr[DIKey] = {
-      tpe.tpe match {
-        case ByNameType(u) =>
-          makeKeyfromRepr(u, id)
-        case o =>
-          makeKeyfromRepr(o, id)
-      }
-    }
-
-    private def makeKeyfromRepr(tpe: TypeRepr, id: Option[String]): Expr[DIKey] = {
-      val safeTypeT = safeTypeFromRepr(tpe)
-      id match {
-        case Some(str) =>
-          val strExpr = Expr(str)
-          '{ new DIKey.IdKey($safeTypeT, $strExpr, None)(scala.compiletime.summonInline[IdContract[String]]) }
-        case None =>
-          '{ new DIKey.TypeKey($safeTypeT) }
-      }
-    }
-
-    def safeType[R: Type]: Expr[SafeType] = {
-      '{ SafeType.get[R](scala.compiletime.summonInline[Tag[R]]) }
-    }
-
-    private def safeType(tpe: TypeTree): Expr[SafeType] = {
-      tpe.tpe match {
-        case ByNameType(u) =>
-          safeTypeFromRepr(u)
-        case o =>
-          safeTypeFromRepr(o)
-      }
-    }
-
-    private def safeTypeFromRepr(tpe: TypeRepr): Expr[SafeType] = {
-      tpe.asType match {
-        case '[a] =>
-          '{ SafeType.get[a](using scala.compiletime.summonInline[Tag[a]]) }
-        case _ =>
-          report.errorAndAbort(s"Cannot generate SafeType from ${tpe.show}, probably that's a bug in Functoid macro")
-      }
-    }
-  }
+  def makeImpl[R: Type](fun: Expr[AnyRef])(using qctx: Quotes): Expr[Functoid[R]] = new FunctoidMacroImpl[qctx.type]().make(fun)
 
   final class FunctoidMacroImpl[Q <: Quotes](using val qctx: Q) {
 
@@ -164,8 +73,9 @@ object FunctoidMacro {
 
     private val paramsMacro = new FunctoidParametersMacro[qctx.type]()
 
-    def make[R: Type](fun: Expr[Any]): Expr[Functoid[R]] = {
-      val out = matchTerm[R](fun.asTerm)
+    def make[R: Type](fun: Expr[AnyRef]): Expr[Functoid[R]] = {
+      val parameters = analyze(fun.asTerm)
+      val out = generateFunctoid[R](parameters, fun)
       report.warning(
         s"""fun=${fun.show}
            |funType=${fun.asTerm.tpe}
@@ -179,64 +89,102 @@ object FunctoidMacro {
       out
     }
 
-    @tailrec
-    def matchTerm[R: Type](fun: Term): Expr[Functoid[R]] = {
-      fun match {
-        case Block(List(defdef), Closure(_, _)) =>
-          matchStatement[R](defdef, fun)
-        case Typed(term, _) =>
-          matchTerm(term)
-        case Inlined(_, _, term) =>
-          matchTerm(term)
-        case Block(_, term) =>
-          matchTerm(term)
-        case _ =>
-          val allTParams = fun.underlying.tpe.typeArgs.map(a => TypeTree.of(using a.asType))
-          val args = allTParams match {
-            case Nil => Nil
-            case o => o.init
-          }
-          analyzeParams[R](args.zipWithIndex.map(a => (s"arg_${a._2}", a._1)), fun)
-      }
+    @tailrec def analyze(fun: Term): List[Expr[LinkedParameter]] = fun match {
+      case Block(List(DefDef(name, (singleParamList: TermParamClause) :: Nil, _, Some(body))), Closure(_, _)) =>
+        analyzeLambdaOrMethodRef(name, singleParamList, body)
+      case Typed(term, _) =>
+        analyze(term)
+      case Inlined(_, _, term) =>
+        analyze(term)
+      case Block(List(), term) =>
+        analyze(term)
+      case otherExpr =>
+        analyzeTypeOfExpr(otherExpr)
     }
 
-    def matchStatement[R: Type](statement: Statement, fun: Term): Expr[Functoid[R]] = {
-      statement match {
-        case DefDef(name, params :: Nil, _, _) =>
-          val paramTypes = params.params.map {
-            case ValDef(name, tpe, _) =>
-              (name, tpe)
-            case p =>
-              report.errorAndAbort(s"Unexpected parameter in $name: ${p.show}")
-          }
-          analyzeParams[R](paramTypes, fun)
+    private def analyzeLambdaOrMethodRef(name: String, singleParamList: TermParamClause, body: Term): List[Expr[LinkedParameter]] = {
+      val methodRefParams = {
+        def go(t: Tree): List[Symbol] = t match {
+          case Apply(f, args) if args.map(_.symbol) == singleParamList.params.map(_.symbol) =>
+            f.symbol.paramSymss.filterNot(_.headOption.exists(_.isTypeParam)).flatten
+          case Inlined(_, _, term) => go(term)
+          case Block(List(), term) => go(term)
+          case Typed(term, _) => go(term)
+          case _ => Nil
+        }
+        go(body)
+      }
+
+      val annotationsOnMethodAreNonEmptyAndASuperset = {
+        methodRefParams.sizeCompare(singleParamList.params) == 0
+        && methodRefParams.exists(_.annotations.nonEmpty)
+      }
+
+//      report.info(
+//        s"""mrefparams = $methodRefParams
+//           |termclause = $singleParamList
+//           |body=$body
+//           |sym=${body match { case Apply(f, _) => f.symbol -> f.symbol.paramSymss; case _ => None }}
+//           |verdict=$annotationsOnMethodAreNonEmptyAndASuperset
+//           |""".stripMargin
+//      )
+
+      // if method reference has more annotations, get parameters from reference instead
+      // to preserve annotations!
+      if (annotationsOnMethodAreNonEmptyAndASuperset) {
+        // Use types from the generated lambda, not the method reference, because method reference types maybe generic/unresolved/unrelated
+        // But lambda params should be sufficiently 'grounded' at this point
+        // (Besides, lambda types are the ones specified by the caller, we should respect them)
+        singleParamList.params.zip(methodRefParams).map {
+          case (ValDef(name, tpeTree, _), mSym) =>
+            paramsMacro.makeParam(name, Left(tpeTree), Some(mSym), Right(mSym.owner.typeRef.memberType(mSym)))
+        }
+      } else {
+        singleParamList.params.map {
+          case valDef @ ValDef(name, tpeTree, _) =>
+            paramsMacro.makeParam(name, Left(tpeTree), Some(valDef.symbol).filterNot(_.isNoSymbol))
+        }
       }
     }
 
-    def analyzeParams[R: Type](params: List[(String, TypeTree)], fun: Term): Expr[Functoid[R]] = {
-      val (paramDefs, paramTypes) = paramsMacro.makeParams[R](params)
+    private def analyzeTypeOfExpr(other: Term): List[Expr[LinkedParameter]] = {
+      val rawTpe = other.underlying.tpe
+      val functionTpe = rawTpe // TODO .baseType
+
+      functionTpe.typeArgs match {
+        case Nil => Nil
+        case o =>
+          val args = o.init
+          args.iterator.zipWithIndex.map {
+            (tpe, idx) =>
+              paramsMacro.makeParam(s"arg_$idx", Right(tpe), None)
+          }.toList
+      }
+    }
 
+    private def generateFunctoid[R: Type](paramDefs: List[Expr[LinkedParameter]], originalFun: Expr[AnyRef]): Expr[Functoid[R]] = {
       '{
-        val rawFn: AnyRef = ${ fun.asExprOf[AnyRef] }
+        val rawFn: AnyRef = ${ originalFun }
         new Functoid[R](
           new ProviderImpl[R](
             ${ Expr.ofList(paramDefs) },
             ${ paramsMacro.safeType[R] },
             rawFn,
-            (args: Seq[Any]) => ${ generateCall(paramTypes, 'rawFn, 'args) },
+            (args: Seq[Any]) => ${ generateRawFnCall(paramDefs.size, 'rawFn, 'args) },
             ProviderType.Function,
           )
         )
       }
     }
 
-    def generateCall(ptypes: List[TypeTree], rawFn: Expr[Any], args: Expr[Seq[Any]]): Expr[Any] = {
-      val params = ptypes.zipWithIndex.map {
-        case (_, idx) =>
+    private def generateRawFnCall(argsCount: Int, rawFn: Expr[Any], args: Expr[Seq[Any]]): Expr[Any] = {
+      val params = List.tabulate(argsCount) {
+        idx =>
           '{ $args(${ Expr(idx) }) }
       }
 
-      val fnType = defn.FunctionClass(ptypes.size).typeRef.appliedTo((0 to ptypes.size).map(_ => TypeRepr.of[Any]).toList)
+      val anyTpe = TypeRepr.of[Any]
+      val fnType = defn.FunctionClass(argsCount).typeRef.appliedTo(List.fill(argsCount + 1)(anyTpe))
 
       val fnAny = fnType.asType match {
         case '[a] =>
@@ -250,4 +198,138 @@ object FunctoidMacro {
 
   }
 
+  final class FunctoidParametersMacro[Q <: Quotes](using val qctx: Q) {
+    import qctx.reflect.*
+
+    private val idAnnotationSym: Symbol = TypeRepr.of[Id].typeSymbol
+
+    extension (t: Either[TypeTree, TypeRepr]) {
+      private def _tpe: TypeRepr = t match {
+        case Right(t) => t
+        case Left(t) => t.tpe
+      }
+    }
+
+    def makeParam(name: String, tpe: Either[TypeTree, TypeRepr], annotSym: Option[Symbol]): Expr[LinkedParameter] = {
+      makeParam(name, tpe, annotSym, tpe)
+    }
+    def makeParam(name: String, tpe: Either[TypeTree, TypeRepr], annotSym: Option[Symbol], annotTpe: Either[TypeTree, TypeRepr]): Expr[LinkedParameter] = {
+      val identifier = (findTypeAnno(annotTpe, idAnnotationSym), annotSym.flatMap(findSymbolAnno(_, idAnnotationSym))) match {
+        case (Some(t), Some(s)) =>
+          report.errorAndAbort(s"Multiple DI annotations on symbol and type at the same time on parameter=$name, typeAnnotation=${t.show} paramAnnotation=${s.show}")
+        case a @ ((Some(_), None) | (None, Some(_))) =>
+          a._1.getOrElse(a._2.getOrElse(throw new RuntimeException("impossible"))) match {
+            case aterm @ Apply(Select(New(_), _), c :: _) =>
+              c.asExprOf[String].value.orElse {
+                report.errorAndAbort(s"distage.Id annotation expects one literal String argument but got ${c.show} in tree ${aterm.show} ($aterm)")
+              }
+            case aterm =>
+              report.errorAndAbort(s"distage.Id annotation expects one literal String argument but got malformed tree ${aterm.show} ($aterm)")
+          }
+//            case _ if annotTpe.toString.contains("Annot") =>
+//              given Printer[Tree] = Printer.TreeStructure
+//              report.errorAndAbort(s"Not annotated $annotTpe show=${annotTpe.fold(_.show, _.show)} show-dealias=${annotTpe.fold(_.show, _.dealias.show)} wtf=${annotTpe match {
+//                  case Right(t @ AnnotatedType(_, term)) => term.show -> findTypeReprAnno(t, idAnnotationSym)
+//                  case Right(t) => s"t=$t"
+//                  case Left(t) => s"annotTpeRepr=${annotTpe._annotTpe} annotTpeReprShow=${annotTpe._annotTpe.show}"
+//                }}")
+        case (None, None) =>
+          None
+      }
+
+      val tpeRepr = tpe._tpe
+
+      val isByName = tpeRepr match {
+        case ByNameType(_) => true
+        case _ => false
+      }
+
+      val wasGeneric = tpeRepr.typeSymbol.isTypeParam // deem abstract type members as generic? No. Because we don't do that in Scala 2 version.
+
+      '{
+        LinkedParameter(
+          SymbolInfo(
+            name = ${ Expr(name) },
+            finalResultType = ${ safeTypeFromRepr(tpeRepr) },
+            isByName = ${ Expr(isByName) },
+            wasGeneric = ${ Expr(wasGeneric) },
+          ),
+          ${ makeKeyFromRepr(tpeRepr, identifier) },
+        )
+      }
+    }
+
+    def safeType[R: Type]: Expr[SafeType] = {
+      '{ SafeType.get[R](using scala.compiletime.summonInline[Tag[R]]) }
+    }
+
+    private def makeKeyFromRepr(tpe: TypeRepr, id: Option[String]): Expr[DIKey] = {
+      val safeTpe = safeTypeFromRepr(tpe)
+      id match {
+        case Some(str) =>
+          val strExpr = Expr(str)
+          '{ new DIKey.IdKey($safeTpe, $strExpr, None)(scala.compiletime.summonInline[IdContract[String]]) }
+        case None =>
+          '{ new DIKey.TypeKey($safeTpe) }
+      }
+    }
+
+    private def safeTypeFromRepr(tpe: TypeRepr): Expr[SafeType] = {
+      dropByName(tpe).asType match {
+        case '[a] =>
+          '{ SafeType.get[a](using scala.compiletime.summonInline[Tag[a]]) }
+        case _ =>
+          report.errorAndAbort(s"Cannot generate SafeType from ${tpe.show}, probably that's a bug in Functoid macro")
+      }
+    }
+
+    private def dropByName(tpe: TypeRepr): TypeRepr = {
+      tpe match {
+        case ByNameType(u) => u
+        case _ => tpe
+      }
+    }
+
+    private def findSymbolAnno(sym: Symbol, annotationSym: Symbol): Option[Term] = {
+      sym.getAnnotation(annotationSym)
+    }
+
+    private def findTypeAnno(t0: Either[TypeTree, TypeRepr], sym: Symbol): Option[Term] = {
+      t0 match {
+        case Right(t) =>
+          findTypeReprAnno(t, sym).orElse(findTypeTreeAnno(TypeTree.of(using t.asType), sym))
+        case Left(t) =>
+          findTypeTreeAnno(t, sym).orElse(findTypeReprAnno(t.tpe, sym))
+      }
+    }
+
+    @tailrec private def findTypeReprAnno(t0: TypeRepr, sym: Symbol): Option[Term] = t0 match {
+      case AnnotatedType(_, aterm) if aterm.tpe.classSymbol.contains(sym) =>
+        Some(aterm)
+      case AnnotatedType(t, _) =>
+        findTypeReprAnno(t, sym)
+      case ByNameType(t) =>
+        findTypeReprAnno(t, sym)
+      case t =>
+        val dealiased = t.dealias.simplified
+        if (t.asInstanceOf[AnyRef] eq dealiased.asInstanceOf[AnyRef]) {
+          None
+        } else {
+          findTypeReprAnno(dealiased, sym)
+        }
+    }
+
+    @tailrec private def findTypeTreeAnno(t: TypeTree, sym: Symbol): Option[Term] = t match {
+      case Annotated(_, aterm) if aterm.tpe.classSymbol.contains(sym) =>
+        Some(aterm)
+      case Annotated(t, _) =>
+        findTypeTreeAnno(t, sym)
+      case ByName(t) =>
+        findTypeTreeAnno(t, sym)
+      case _ =>
+        None
+    }
+
+  }
+
 }
diff --git a/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/Lifecycle.scala b/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/Lifecycle.scala
index 5ad1d45a1..a8f7fab0f 100644
--- a/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/Lifecycle.scala
+++ b/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/Lifecycle.scala
@@ -454,7 +454,7 @@ object Lifecycle extends LifecycleInstances {
   }
 
   /** Convert [[cats.effect.Resource]] to [[Lifecycle]] */
-  def fromCats[F[_], A](@unused resource: Resource[F, A])(implicit F: Sync[F]): Lifecycle.FromCats[F, A] = {
+  def fromCats[F[_], A](resource: Resource[F, A])(implicit F: Sync[F]): Lifecycle.FromCats[F, A] = {
     new FromCats[F, A] {
       override def acquire: F[kernel.Ref[F, List[F[Unit]]]] = {
         kernel.Ref.of[F, List[F[Unit]]](Nil)(kernel.Ref.Make.syncInstance(F))
diff --git a/distage/distage-core-api/src/main/scala/izumi/distage/model/providers/Functoid.scala b/distage/distage-core-api/src/main/scala/izumi/distage/model/providers/Functoid.scala
index 287cb02bd..8e7257d4e 100644
--- a/distage/distage-core-api/src/main/scala/izumi/distage/model/providers/Functoid.scala
+++ b/distage/distage-core-api/src/main/scala/izumi/distage/model/providers/Functoid.scala
@@ -1,5 +1,6 @@
 package izumi.distage.model.providers
 
+import izumi.distage.constructors.{AnyConstructor, ClassConstructor, FactoryConstructor, HasConstructor, TraitConstructor}
 import izumi.distage.model.definition.Identifier
 import izumi.distage.model.exceptions.TODOBindingException
 import izumi.distage.model.reflection.LinkedParameter
@@ -91,7 +92,7 @@ final case class Functoid[+A](get: Provider) {
   /**
     * Applicative's `ap` method - can be used to chain transformations like `flatMap`.
     *
-    * Apply a function produced by `that` Provider to the value produced by `this` Provider.
+    * Apply a function produced by `that` Functoid to the value produced by `this` Functoid.
     *
     * Same as
     * {{{
@@ -103,7 +104,7 @@ final case class Functoid[+A](get: Provider) {
   }
 
   /**
-    * Apply a function produced by `this` Provider to the argument produced by `that` Provider.
+    * Apply a function produced by `this` Functoid to the argument produced by `that` Functoid.
     *
     * Same as
     * {{{
@@ -114,7 +115,7 @@ final case class Functoid[+A](get: Provider) {
     map2(that)((f, a) => f.asInstanceOf[B => C](a))
   }
 
-  /** Add `B` as an unused dependency of this Provider */
+  /** Add `B` as an unused dependency of this Functoid */
   def addDependency[B: Tag]: Functoid[A] = addDependency(DIKey[B])
   def addDependency[B: Tag](name: Identifier): Functoid[A] = addDependency(DIKey[B](name))
   def addDependency(key: DIKey): Functoid[A] = addDependencies(key :: Nil)
@@ -175,6 +176,33 @@ object Functoid extends FunctoidMacroMethods {
     )
   }
 
+  /** Derive constructor for a concrete class `A` using [[ClassConstructor]] */
+  def makeClass[A: ClassConstructor]: Functoid[A] = ClassConstructor[A]
+
+  /**
+    * Derive constructor for an abstract class or a trait `A` using [[TraitConstructor]]
+    *
+    * @see [[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]]
+    */
+  def makeTrait[A: TraitConstructor]: Functoid[A] = TraitConstructor[A]
+
+  /**
+    * Derive constructor for a "factory-like" abstract class or a trait `A` using [[FactoryConstructor]]
+    *
+    * @see [[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]]
+    */
+  def makeFactory[A: FactoryConstructor]: Functoid[A] = FactoryConstructor[A]
+
+  /**
+    * Derive constructor for a `zio.Has` value `A` using [[HasConstructor]]
+    *
+    * @see [[https://izumi.7mind.io/distage/basics.html#zio-has-bindings ZIO Has bindings]]
+    */
+  def makeHas[A: HasConstructor]: Functoid[A] = HasConstructor[A]
+
+  /** Derive constructor for a type `A` using [[AnyConstructor]] */
+  def makeAny[A: AnyConstructor]: Functoid[A] = AnyConstructor[A]
+
   def todoProvider(key: DIKey)(implicit pos: CodePositionMaterializer): Functoid[Nothing] = {
     new Functoid[Nothing](
       Provider.ProviderImpl(
diff --git a/distage/distage-core/src/main/scala/izumi/distage/planning/solver/GraphPreparations.scala b/distage/distage-core/src/main/scala/izumi/distage/planning/solver/GraphPreparations.scala
index c8700a1e7..f143b476a 100644
--- a/distage/distage-core/src/main/scala/izumi/distage/planning/solver/GraphPreparations.scala
+++ b/distage/distage-core/src/main/scala/izumi/distage/planning/solver/GraphPreparations.scala
@@ -9,6 +9,7 @@ import izumi.distage.model.planning.AxisPoint
 import izumi.distage.model.reflection.DIKey
 import izumi.distage.planning.BindingTranslator
 import izumi.distage.planning.solver.SemigraphSolver.SemiEdgeSeq
+import izumi.fundamentals.collections.MutableMultiMap
 import izumi.fundamentals.graphs.WeakEdge
 import izumi.fundamentals.graphs.struct.IncidenceMatrix
 import izumi.fundamentals.graphs.tools.gc.Tracer
@@ -23,18 +24,11 @@ class GraphPreparations(
   import scala.collection.compat._
 
   def findWeakSetMembers(
-    sets: Map[Annotated[DIKey], Node[DIKey, InstantiationOp]],
-    matrix: SemiEdgeSeq[Annotated[DIKey], DIKey, InstantiationOp],
+    setOps: Map[Annotated[DIKey], Node[DIKey, InstantiationOp]],
+    execOpIndex: MutableMultiMap[DIKey, InstantiationOp],
     roots: Set[DIKey],
   ): Set[WeakEdge[DIKey]] = {
-    import izumi.fundamentals.collections.IzCollections._
-
-    val indexed = matrix.links.map {
-      case (successor, node) =>
-        (successor.key, node.meta)
-    }.toMultimapMut
-
-    val setDefs = sets
+    val setDefs = setOps
       .collect {
         case (target, Node(_, s: CreateSet)) =>
           (target, s.members)
@@ -46,14 +40,27 @@ class GraphPreparations(
           .diff(roots)
           .flatMap {
             member =>
-              indexed.get(member).toSeq.flatten.collect {
-                case ExecutableOp.WiringOp.ReferenceKey(_, Wiring.SingletonWiring.Reference(_, referenced, true), _) =>
-                  WeakEdge(referenced, member)
-              }
+              getSetElementWeakEdges(execOpIndex, member)
           }
     }.toSet
   }
 
+  def executableOpIndex(matrix: SemiEdgeSeq[Annotated[DIKey], DIKey, InstantiationOp]): MutableMultiMap[DIKey, InstantiationOp] = {
+    import izumi.fundamentals.collections.IzCollections._
+
+    matrix.links.map {
+      case (successor, node) =>
+        (successor.key, node.meta)
+    }.toMultimapMut
+  }
+
+  def getSetElementWeakEdges(execOpIndex: MutableMultiMap[DIKey, InstantiationOp], member: DIKey): Iterator[WeakEdge[DIKey]] = {
+    execOpIndex.getOrElse(member, Nil).iterator.collect {
+      case ExecutableOp.WiringOp.ReferenceKey(_, Wiring.SingletonWiring.Reference(_, referenced, true), _) =>
+        WeakEdge(referenced, member)
+    }
+  }
+
   def getRoots(input: Roots, allOps: Seq[(Annotated[DIKey], InstantiationOp)]): Set[DIKey] = {
     val effective = input match {
       case Roots.Of(roots) =>
diff --git a/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanSolver.scala b/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanSolver.scala
index e249b7a49..8f0951f01 100644
--- a/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanSolver.scala
+++ b/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanSolver.scala
@@ -97,7 +97,7 @@ object PlanSolver {
         preps.getRoots(input.roots, allOps)
 
       val weakSetMembers: Set[WeakEdge[DIKey]] =
-        preps.findWeakSetMembers(sets, matrix, roots)
+        preps.findWeakSetMembers(sets, preps.executableOpIndex(matrix), roots)
 
       Right(Problem(activations, matrix, roots, weakSetMembers))
     }
diff --git a/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanVerifier.scala b/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanVerifier.scala
index 8cf4fa536..daf3c08a7 100644
--- a/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanVerifier.scala
+++ b/distage/distage-core/src/main/scala/izumi/distage/planning/solver/PlanVerifier.scala
@@ -11,14 +11,13 @@ import izumi.distage.model.planning.{ActivationChoices, AxisPoint}
 import izumi.distage.model.reflection.DIKey.SetElementKey
 import izumi.distage.model.reflection.{DIKey, SafeType}
 import izumi.distage.planning.BindingTranslator
-import izumi.distage.planning.solver.PlanVerifier.PlanIssue._
+import izumi.distage.planning.solver.PlanVerifier.PlanIssue.*
 import izumi.distage.planning.solver.PlanVerifier.{PlanIssue, PlanVerifierResult}
 import izumi.distage.planning.solver.SemigraphSolver.SemiEdgeSeq
-import izumi.functional.IzEither._
-import izumi.fundamentals.collections.ImmutableMultiMap
-import izumi.fundamentals.collections.IzCollections._
+import izumi.functional.IzEither.*
+import izumi.fundamentals.collections.{ImmutableMultiMap, MutableMultiMap}
+import izumi.fundamentals.collections.IzCollections.*
 import izumi.fundamentals.collections.nonempty.{NonEmptyList, NonEmptyMap, NonEmptySet}
-import izumi.fundamentals.graphs.WeakEdge
 import izumi.fundamentals.platform.strings.IzString.toRichIterable
 
 import java.util.concurrent.TimeUnit
@@ -69,7 +68,7 @@ class PlanVerifier(
     val justMutators = mutators.map { case (k, op, _) => (k.key, (op, k.axis)) }.toMultimap
 
     val rootKeys: Set[DIKey] = preps.getRoots(roots, justOps)
-    val weakSetMembers: Set[WeakEdge[DIKey]] = preps.findWeakSetMembers(setOps.toMap, matrix, rootKeys)
+    val execOpIndex: MutableMultiMap[DIKey, InstantiationOp] = preps.executableOpIndex(matrix)
 
     val mutVisited = mutable.HashSet.empty[(DIKey, Set[AxisPoint])]
     val effectType = SafeType.getK[F]
@@ -78,7 +77,7 @@ class PlanVerifier(
     var after = before
     val issues =
       try {
-        trace(allAxis, mutVisited, matrixToTrace, weakSetMembers, justMutators, providedKeys, excludedActivations, rootKeys, effectType)
+        trace(allAxis, mutVisited, matrixToTrace, execOpIndex, justMutators, providedKeys, excludedActivations, rootKeys, effectType)
       } finally {
         after = System.currentTimeMillis()
       }
@@ -96,7 +95,7 @@ class PlanVerifier(
     allAxis: Map[String, Set[String]],
     allVisited: mutable.HashSet[(DIKey, Set[AxisPoint])],
     matrix: ImmutableMultiMap[DIKey, (InstantiationOp, Set[AxisPoint])],
-    weakSetMembers: Set[WeakEdge[DIKey]],
+    execOpIndex: MutableMultiMap[DIKey, InstantiationOp],
     justMutators: ImmutableMultiMap[DIKey, (InstantiationOp, Set[AxisPoint])],
     providedKeys: DIKey => Boolean,
     excludedActivations: Set[NonEmptySet[AxisPoint]],
@@ -179,11 +178,11 @@ class PlanVerifier(
                   } else {
                     Right(())
                   }
-                next <- checkConflicts(allAxis, opsWithMergedSets, weakSetMembers, excludedActivations, effectType)
+                next <- checkConflicts(allAxis, opsWithMergedSets, execOpIndex, excludedActivations, effectType)
               } yield {
                 allVisited.add((key, currentActivation))
 
-                val mutators = justMutators.getOrElse(key, Set.empty).iterator.filter(ac allValid _._2).flatMap(m => depsOf(weakSetMembers)(m._1)).toSeq
+                val mutators = justMutators.getOrElse(key, Set.empty).iterator.filter(ac allValid _._2).flatMap(m => depsOf(execOpIndex)(m._1)).toSeq
 
                 val goNext = next.iterator.map {
                   case (nextActivation, nextDeps) =>
@@ -249,7 +248,7 @@ class PlanVerifier(
   protected[this] def checkConflicts(
     allAxis: Map[String, Set[String]],
     withoutCurrentActivations: Set[(InstantiationOp, Set[AxisPoint], Set[AxisPoint])],
-    weakSetMembers: Set[WeakEdge[DIKey]],
+    execOpIndex: MutableMultiMap[DIKey, InstantiationOp],
     excludedActivations: Set[NonEmptySet[AxisPoint]],
     effectType: SafeType,
   ): Either[List[PlanIssue], Seq[(Set[AxisPoint], Set[DIKey])]] = {
@@ -267,7 +266,7 @@ class PlanVerifier(
       val next = withoutCurrentActivations.iterator.map {
         case (op, activations, _) =>
           // TODO: I'm not sure if it's "correct" to "activate" all the points together but it simplifies things greatly
-          val deps = depsOf(weakSetMembers)(op)
+          val deps = depsOf(execOpIndex)(op)
           val acts = op match {
             case _: ExecutableOp.CreateSet =>
               Set.empty[AxisPoint]
@@ -280,13 +279,16 @@ class PlanVerifier(
     }
   }
 
-  protected[this] final def depsOf(weakSetMembers: Set[WeakEdge[DIKey]])(op: InstantiationOp): Set[DIKey] = {
+  protected[this] final def depsOf(
+    execOpIndex: MutableMultiMap[DIKey, InstantiationOp]
+  )(op: InstantiationOp
+  ): Set[DIKey] = {
     op match {
       case cs: CreateSet =>
         // we completely ignore weak members, they don't make any difference in case they are unreachable through other paths
         val members = cs.members.filter {
           case m: SetElementKey =>
-            !weakSetMembers.contains(WeakEdge(m.reference, m))
+            preps.getSetElementWeakEdges(execOpIndex, m).isEmpty
           case _ =>
             true
         }
diff --git a/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/bundled/PostgresFlyWayDocker.scala b/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/bundled/PostgresFlyWayDocker.scala
index f2b571e4c..ccf913f11 100644
--- a/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/bundled/PostgresFlyWayDocker.scala
+++ b/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/bundled/PostgresFlyWayDocker.scala
@@ -1,6 +1,6 @@
 package izumi.distage.docker.bundled
 
-import distage.{Id, ModuleDef, TagK}
+import distage.{Functoid, Id, ModuleDef, TagK}
 import izumi.distage.docker.Docker.{DockerPort, DockerReusePolicy, Mount}
 import izumi.distage.docker.healthcheck.ContainerHealthCheck
 import izumi.distage.docker.{ContainerDef, ContainerNetworkDef}
@@ -100,10 +100,13 @@ class PostgresFlyWayDockerModule[F[_]: TagK](
       .make[F]
       .connectToNetwork(PostgresFlyWayDocker.FlyWayNetwork)
       .modifyConfig {
-        (postgresContainer: PostgresFlyWayDocker.Container @Id("postgres-flyway-proxy"), cfg: PostgresFlyWayDocker.Cfg) => (config: PostgresFlyWayDocker.FlyWay.Config) =>
-          PostgresFlyWayDocker.FlyWay.applyCfg(postgresContainer.hostName, cfg)(
-            config
-          ): PostgresFlyWayDocker.FlyWay.Config // TODO: https://github.com/lampepfl/dotty/issues/16107 / 16108
+        Functoid.apply { // TODO: https://github.com/lampepfl/dotty/issues/16108
+          (postgresContainer: PostgresFlyWayDocker.Container @Id("postgres-flyway-proxy"), cfg: PostgresFlyWayDocker.Cfg) =>
+            (config: PostgresFlyWayDocker.FlyWay.Config) =>
+              PostgresFlyWayDocker.FlyWay.applyCfg(postgresContainer.hostName, cfg)(
+                config
+              ): PostgresFlyWayDocker.FlyWay.Config // TODO: https://github.com/lampepfl/dotty/issues/16107 / 16108
+        }
       }
   }
   // Binding of the actual Postgres container with the FlyWay container dependency.
diff --git a/doc/microsite/src/main/tut/distage/basics.md b/doc/microsite/src/main/tut/distage/basics.md
index 232363279..405e51cad 100644
--- a/doc/microsite/src/main/tut/distage/basics.md
+++ b/doc/microsite/src/main/tut/distage/basics.md
@@ -1227,7 +1227,8 @@ Injector().produceRun(module overriddenBy new ModuleDef {
 
 ## Auto-Factories
 
-`distage` can instantiate 'factory' classes from suitable traits. This feature is especially useful with `Akka`.
+`distage` can derive 'factory' implementations from suitable traits using `makeFactory` method.
+This feature is especially useful with `Akka`.
 All unimplemented methods _with parameters_ in a trait will be filled by factory methods:
 
 Given a class `ActorFactory`:
@@ -1266,7 +1267,7 @@ class ActorFactoryImpl(sessionStorage: SessionStorage) extends ActorFactory {
 }
 ```
 
-Since `distage` version `1.1.0` parameterless methods in factories just produce new instances and you have to bind factories explicitly using `makeFactory` method.
+Since `distage` version `1.1.0` you have to bind factories explicitly using `makeFactory` and `fromFactory` methods, not implicitly via `make`; parameterless methods in factories now produce new instances instead of summoning a dependency.
 
 ### @With annotation
 
@@ -1295,7 +1296,7 @@ object Actor {
 }
 
 def factoryModule = new ModuleDef {
-  make[Actor.Factory]
+  makeFactory[Actor.Factory]
   make[Actor.Configuration].from(Actor.Configuration(allCaps = false))
 }
 
diff --git a/project/Deps.sc b/project/Deps.sc
index 5346ddd94..ce9419223 100644
--- a/project/Deps.sc
+++ b/project/Deps.sc
@@ -142,7 +142,7 @@ object Izumi {
   // DON'T REMOVE, these variables are read from CI build (build.sh)
   final val scala212 = ScalaVersion("2.12.16")
   final val scala213 = ScalaVersion("2.13.8")
-  final val scala300 = ScalaVersion("3.2.1-RC2")
+  final val scala300 = ScalaVersion("3.2.1")
 
   object Groups {
     final val fundamentals = Set(Group("fundamentals"))
diff --git a/project/Versions.scala b/project/Versions.scala
index c0eadb0c0..d5dec9249 100644
--- a/project/Versions.scala
+++ b/project/Versions.scala
@@ -1,6 +1,6 @@
 object V {
   // izumi
-  val izumi_reflect = "2.2.0"
+  val izumi_reflect = "2.2.1"
 
   // foundation
   val collection_compat = "2.8.1"
diff --git a/project/build.properties b/project/build.properties
index 1cfbe550d..f5b9ea742 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1,1 +1,1 @@
-sbt.version = 1.7.2
+sbt.version = 1.7.3


Test patch:
diff --git a/distage/distage-core/src/test/scala-3/izumi/distage/injector/Scala3AutoTraitsTest.scala b/distage/distage-core/src/test/scala-3/izumi/distage/injector/Scala3AutoTraitsTest.scala
index 4920aa43b..4cba94480 100644
--- a/distage/distage-core/src/test/scala-3/izumi/distage/injector/Scala3AutoTraitsTest.scala
+++ b/distage/distage-core/src/test/scala-3/izumi/distage/injector/Scala3AutoTraitsTest.scala
@@ -23,6 +23,7 @@ class Scala3AutoTraitsTest extends AnyWordSpec with MkInjector {
 
       assert(aclass2.c == 5)
 
+      // support by-name
       val traitCtor = TraitConstructor[ATrait].get
       val atrait = traitCtor.unsafeApply(Seq(TypedRef.byName(5))).asInstanceOf[ATrait]
 
@@ -65,6 +66,7 @@ class Scala3AutoTraitsTest extends AnyWordSpec with MkInjector {
       assert(atraitWithCtor.b == 4)
       assert(atraitWithCtor.c == 2)
 
+// generated code:
 //  ((a: scala.Int, c: scala.Int, `a₂`: scala.Int, b: scala.Int) =>
 //    class TraitInheritingAbstractClassAndTraitWithConstructorAutoImpl
 //      extends izumi.distage.fixtures.Scala3TraitCases.AbstractClassWithTraitWithConstructor(a, c)
diff --git a/distage/distage-core/src/test/scala/izumi/distage/impl/FunctoidTest.scala b/distage/distage-core/src/test/scala/izumi/distage/impl/FunctoidTest.scala
index 72feb3c17..802a0f0c7 100644
--- a/distage/distage-core/src/test/scala/izumi/distage/impl/FunctoidTest.scala
+++ b/distage/distage-core/src/test/scala/izumi/distage/impl/FunctoidTest.scala
@@ -137,6 +137,12 @@ class FunctoidTest extends AnyWordSpec {
       assert(fn.diKeys contains DIKey.get[Int].named("defargann2"))
     }
 
+    "do not get confused by a swap lambda method reference with argument annotations" in {
+      val fn = Functoid.apply((x, y) => defargannfn(y, x)).get
+
+      assert(fn.diKeys == Seq(DIKey.get[Int], DIKey.get[String]))
+    }
+
     "handle polymorphic functions" in {
       val fn1 = Functoid.apply(poly[List] _).get
 
@@ -375,8 +381,15 @@ class FunctoidTest extends AnyWordSpec {
     }
 
     "fail on multiple conflicting annotations on the same parameter" in {
-      assertTypeError("Functoid.apply(defconfannfn _)")
-      assertTypeError("Functoid.apply(defconfannfn2 _)")
+      val t1 = intercept[TestFailedException] {
+        assertCompiles("Functoid.apply(defconfannfn _)")
+      }
+      assert(t1.getMessage contains "Multiple DI annotations on symbol")
+
+      val t2 = intercept[TestFailedException] {
+        assertCompiles("Functoid.apply(defconfannfn2 _)")
+      }
+      assert(t2.getMessage contains "Multiple DI annotations on symbol")
     }
 
     "extract Id annotations from higher-kinded type aliases" in {
diff --git a/distage/distage-core/src/test/scala/izumi/distage/injector/FactoriesTest.scala b/distage/distage-core/src/test/scala/izumi/distage/injector/FactoriesTest.scala
index fde9edad4..3cd8dfae8 100644
--- a/distage/distage-core/src/test/scala/izumi/distage/injector/FactoriesTest.scala
+++ b/distage/distage-core/src/test/scala/izumi/distage/injector/FactoriesTest.scala
@@ -116,7 +116,7 @@ class FactoriesTest extends AnyWordSpec with MkInjector {
     assert(instantiated.nonAssisted(dependency).b eq dependency)
   }
 
-  "handle assisted abstract factories with multiple parameters of the same type" in {
+  "handle assisted abstract factories with multiple parameters of the same type with names matching constructor" in {
     import FactoryCase2._
 
     val definition = PlannerInput.everything(new ModuleDef {
diff --git a/distage/distage-core/src/test/scala/izumi/distage/injector/PlanVerifierTest.scala b/distage/distage-core/src/test/scala/izumi/distage/injector/PlanVerifierTest.scala
index 66ed5b709..310a41c8a 100644
--- a/distage/distage-core/src/test/scala/izumi/distage/injector/PlanVerifierTest.scala
+++ b/distage/distage-core/src/test/scala/izumi/distage/injector/PlanVerifierTest.scala
@@ -15,6 +15,7 @@ import org.scalatest.exceptions.TestFailedException
 import org.scalatest.wordspec.AnyWordSpec
 
 class PlanVerifierTest extends AnyWordSpec with MkInjector {
+
   "Verifier handles resources (uniform case, Roots.Everything, https://github.com/7mind/izumi/issues/1476)" in {
     import PlanVerifierCase1._
 
@@ -602,4 +603,18 @@ class PlanVerifierTest extends AnyWordSpec with MkInjector {
     val result = PlanVerifier().verify[Identity](definition, Roots.target[Set[Fork2]], Injector.providedKeys(), Set.empty)
     assert(result.issues.isEmpty)
   }
+
+  "Verifier handles weak sets: named weak references are still weak" in {
+    import PlanVerifierCase1._
+
+    val definition = new ModuleDef {
+      many[Fork1]
+        .weak[ImplA]
+        .weak[ImplB]("missing")
+    }
+
+    val result = PlanVerifier().verify[Identity](definition, Roots.target[Set[Fork1]], Injector.providedKeys(), Set.empty)
+    assert(result.issues.isEmpty)
+  }
+
 }