1
1
package mill .api .internal
2
2
3
+ import mill .api .Task
3
4
import mill .api .daemon .internal .internal
4
5
import scala .annotation .compileTimeOnly
5
6
@@ -24,15 +25,69 @@ object Applicative {
24
25
25
26
type Id [+ T ] = T
26
27
28
+ /**
29
+ * @param allowNestedTasks whether `Task[Task[A]]` or `Task[Something[Task[A]]]` and similar structures
30
+ * are allowed.
31
+ */
27
32
private [mill] def impl [M [_]: Type , W [_]: Type , Z [_]: Type , T : Type , Ctx : Type ](using
28
33
Quotes
29
34
)(
30
35
traverseCtx : (Expr [Seq [W [Any ]]], Expr [(Seq [Any ], Ctx ) => Z [T ]]) => Expr [M [T ]],
31
36
t : Expr [Z [T ]],
32
- allowTaskReferences : Boolean = true
37
+ allowTaskReferences : Boolean = true ,
38
+ allowNestedTasks : Boolean = false
33
39
): Expr [M [T ]] = {
34
40
import quotes .reflect .*
35
41
42
+ def checkForNestedTasks () = {
43
+ val taskType = TypeRepr .of[Task [_]]
44
+
45
+ def isTask (tpr : TypeRepr ): Boolean =
46
+ // We use `typeSymbol` for a robust check.
47
+ tpr.typeSymbol == taskType.typeSymbol
48
+
49
+ def containsTask (tpr : TypeRepr ): Boolean = {
50
+ // Dealias to handle type aliases like `type MyTask[A] = Task[A]`
51
+ val currentType = tpr.dealias
52
+
53
+ if (isTask(currentType)) true
54
+ else
55
+ // Otherwise, check all of its type arguments recursively.
56
+ // e.g., for Map[Int, String], this would be [Int, String]
57
+ currentType.typeArgs.exists(containsTask)
58
+ }
59
+
60
+ val resultTypeRepr = TypeRepr .of[M [T ]].dealias
61
+
62
+ if (isTask(resultTypeRepr)) {
63
+
64
+ /** The `A` to the `Task[A]`. */
65
+ val innerTypeRepr = resultTypeRepr.typeArgs match {
66
+ case innerTypeRepr :: Nil => innerTypeRepr
67
+ case other => throw new IllegalStateException (
68
+ s " Task[_] should have exactly one type parameter, but got ${other.map(_.show)}. " +
69
+ " This is a bug in mill."
70
+ )
71
+ }
72
+
73
+ if (containsTask(innerTypeRepr)) {
74
+ report.errorAndAbort(
75
+ // report.warning(
76
+ s """ |A `Task[A]` cannot be a parameter of another `Task[A]` because the inner task would not
77
+ |be executed.
78
+ |
79
+ |See https://github.com/com-lihaoyi/mill/issues/5263 for more information.
80
+ |
81
+ |Type of the result: ${resultTypeRepr.show}
82
+ | """ .stripMargin,
83
+ Position .ofMacroExpansion
84
+ )
85
+ }
86
+ }
87
+ }
88
+
89
+ if (! allowNestedTasks) checkForNestedTasks()
90
+
36
91
val targetApplySym = TypeRepr .of[Applyable [Nothing , ? ]].typeSymbol.methodMember(" apply" ).head
37
92
38
93
// Derived from @olafurpg's
@@ -99,6 +154,7 @@ object Applicative {
99
154
100
155
val newBody = treeMap.transformTree(t.asTerm)(Symbol .spliceOwner).asExprOf[Z [T ]]
101
156
val exprsList = Expr .ofList(exprs.toList.map(_.asExprOf[W [Any ]]))
157
+
102
158
(newBody, exprsList)
103
159
}
104
160
@@ -119,5 +175,4 @@ object Applicative {
119
175
else
120
176
traverseCtx(exprsList, callback)
121
177
}
122
-
123
178
}
0 commit comments