1
+ package dotty .tools
2
+ package dotc
3
+ package transform
4
+
5
+ import core ._
6
+ import Contexts ._ , Symbols ._ , Types ._ , Annotations ._ , Constants ._
7
+ import StdNames .nme
8
+ import ast .untpd
9
+ import ast .tpd ._
10
+ import config .Config
11
+
12
+ object ContextFunctionResults :
13
+
14
+ /** Annotate methods that have context function result types directly matched by context
15
+ * closures on their right-hand side. Parameters to such closures will be integrated
16
+ * as additional method parameters in erasure.
17
+ */
18
+ def annotateContextResults (mdef : DefDef )(using Context ): Unit =
19
+ def contextResultCount (rhs : Tree , tp : Type ): Int = tp match
20
+ case defn.ContextFunctionType (_, resTpe, _) =>
21
+ rhs match
22
+ case closureDef(meth) => 1 + contextResultCount(meth.rhs, resTpe)
23
+ case _ => 0
24
+ case _ => 0
25
+
26
+ val meth = mdef.symbol
27
+
28
+ // Disable context result annotations for anonymous functions
29
+ // and for implementations of PolyFunction
30
+ def disabled =
31
+ meth.isAnonymousFunction
32
+ || meth.name == nme.apply
33
+ && meth.owner.isAnonymousClass
34
+ && meth.owner.info.parents.exists(_.isRef(defn.PolyFunctionClass ))
35
+
36
+ val count = contextResultCount(mdef.rhs, mdef.tpt.tpe)
37
+
38
+ if Config .flattenContextFunctionResults && count != 0 && ! disabled then
39
+ val countAnnot = Annotation (defn.ContextResultCountAnnot , Literal (Constant (count)))
40
+ mdef.symbol.addAnnotation(countAnnot)
41
+ end annotateContextResults
42
+
43
+ /** The argument of a ContextResultCount annotation, or 0 if none exists.
44
+ * See PostTyper#annotateContextResults.
45
+ */
46
+ def contextResultCount (sym : Symbol )(using Context ): Int =
47
+ sym.getAnnotation(defn.ContextResultCountAnnot ) match
48
+ case Some (annot) =>
49
+ val ast .Trees .Literal (Constant (crCount : Int )) :: Nil : @ unchecked = annot.arguments
50
+ crCount
51
+ case none => 0
52
+
53
+ /** Turn the first `crCount` context function types in the result type of `tp`
54
+ * into the curried method types.
55
+ */
56
+ def integrateContextResults (tp : Type , crCount : Int )(using Context ): Type =
57
+ if crCount == 0 then tp
58
+ else tp match
59
+ case ExprType (rt) =>
60
+ integrateContextResults(rt, crCount)
61
+ case tp : MethodOrPoly =>
62
+ tp.derivedLambdaType(resType = integrateContextResults(tp.resType, crCount))
63
+ case defn.ContextFunctionType (argTypes, resType, isErased) =>
64
+ val methodType : MethodTypeCompanion =
65
+ if isErased then ErasedMethodType else MethodType
66
+ methodType(argTypes, integrateContextResults(resType, crCount - 1 ))
67
+
68
+ /** The total number of parameters of method `sym`, not counting
69
+ * erased parameters, but including context result parameters.
70
+ */
71
+ def totalParamCount (sym : Symbol )(using Context ): Int =
72
+
73
+ def contextParamCount (tp : Type , crCount : Int ): Int =
74
+ if crCount == 0 then 0
75
+ else
76
+ val defn .ContextFunctionType (params, resTpe, isErased): @ unchecked = tp
77
+ val rest = contextParamCount(resTpe, crCount - 1 )
78
+ if isErased then rest else params.length + rest
79
+
80
+ def normalParamCount (tp : Type ): Int = tp.widenExpr.stripPoly match
81
+ case mt @ MethodType (pnames) =>
82
+ val rest = normalParamCount(mt.resType)
83
+ if mt.isErasedMethod then rest else pnames.length + rest
84
+ case _ => contextParamCount(tp, contextResultCount(sym))
85
+
86
+ normalParamCount(sym.info)
87
+ end totalParamCount
88
+
89
+ /** The rightmost context function type in the result type of `meth`
90
+ * that represents `paramCount` curried, non-erased parameters that
91
+ * are included in the `contextResultCount` of `meth`.
92
+ * Example:
93
+ *
94
+ * Say we have `def m(x: A): B ?=> (C1, C2, C3) ?=> D ?=> E ?=> F`,
95
+ * paramCount == 4, and the contextResultCount of `m` is 3.
96
+ * Then we return the type `(C1, C2, C3) ?=> D ?=> E ?=> F`, since this
97
+ * type covers the 4 rightmost parameters C1, C2, C3 and D before the
98
+ * contextResultCount runs out at E ?=> F.
99
+ * Erased parameters are ignored; they contribute nothing to the
100
+ * parameter count.
101
+ */
102
+ def contextFunctionResultTypeCovering (meth : Symbol , paramCount : Int )(using ctx : Context ) =
103
+ given Context = ctx.withPhase(ctx.erasurePhase)
104
+
105
+ // Recursive instances return pairs of context types and the
106
+ // # of parameters they represent.
107
+ def missingCR (tp : Type , crCount : Int ): (Type , Int ) =
108
+ if crCount == 0 then (tp, 0 )
109
+ else
110
+ val defn .ContextFunctionType (formals, resTpe, isErased): @ unchecked = tp
111
+ val result @ (rt, nparams) = missingCR(resTpe, crCount - 1 )
112
+ assert(nparams <= paramCount)
113
+ if nparams == paramCount || isErased then result
114
+ else (tp, nparams + formals.length)
115
+ missingCR(meth.info.finalResultType, contextResultCount(meth))._1
116
+ end contextFunctionResultTypeCovering
117
+
118
+ /** Should selection `tree` be eliminated since it refers to an `apply`
119
+ * node of a context function type whose parameters will end up being
120
+ * integrated in the preceding method?
121
+ * @param `n` the select nodes seen in previous recursive iterations of this method
122
+ */
123
+ def integrateSelect (tree : untpd.Tree , n : Int = 0 )(using ctx : Context ): Boolean =
124
+ if ctx.erasedTypes then
125
+ integrateSelect(tree, n)(using ctx.withPhase(ctx.erasurePhase))
126
+ else tree match
127
+ case Select (qual, name) =>
128
+ if name == nme.apply && defn.isContextFunctionClass(tree.symbol.maybeOwner) then
129
+ integrateSelect(qual, n + 1 )
130
+ else
131
+ n > 0 && contextResultCount(tree.symbol) >= n
132
+ case Ident (name) =>
133
+ n > 0 && contextResultCount(tree.symbol) >= n
134
+ case Apply (fn, args) =>
135
+ integrateSelect(fn, n)
136
+ case TypeApply (fn, _) =>
137
+ integrateSelect(fn, n)
138
+ case Block (_, expr) =>
139
+ integrateSelect(expr, n)
140
+ case Inlined (_, _, expr) =>
141
+ integrateSelect(expr, n)
142
+ case _ =>
143
+ false
144
+
145
+ end ContextFunctionResults
0 commit comments