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