Skip to content

Commit 04dacad

Browse files
committed
Optimize function tests in NameOps
1 parent a01d52a commit 04dacad

File tree

2 files changed

+72
-40
lines changed

2 files changed

+72
-40
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,7 +1389,7 @@ class Definitions {
13891389
!tp.isInstanceOf[RefinedType]
13901390
}
13911391

1392-
/** Is `tp` a representation of a (possibly depenent) function type or an alias of such? */
1392+
/** Is `tp` a representation of a (possibly dependent) function type or an alias of such? */
13931393
def isFunctionType(tp: Type)(using Context): Boolean =
13941394
isNonRefinedFunction(tp.dropDependentRefinement)
13951395

@@ -1440,13 +1440,12 @@ class Definitions {
14401440
* - the upper bound of a TypeParamRef in the current constraint
14411441
*/
14421442
def asContextFunctionType(tp: Type)(using Context): Type =
1443-
tp.stripTypeVar.dealias match {
1443+
tp.stripTypeVar.dealias match
14441444
case tp1: TypeParamRef if ctx.typerState.constraint.contains(tp1) =>
14451445
asContextFunctionType(TypeComparer.bounds(tp1).hiBound)
14461446
case tp1 =>
1447-
if (isFunctionType(tp1) && tp1.typeSymbol.name.isContextFunction) tp1
1447+
if tp1.typeSymbol.name.isContextFunction && isFunctionType(tp1) then tp1
14481448
else NoType
1449-
}
14501449

14511450
/** Is `tp` an context function type? */
14521451
def isContextFunctionType(tp: Type)(using Context): Boolean =
@@ -1468,7 +1467,7 @@ class Definitions {
14681467
else None
14691468

14701469
def isErasedFunctionType(tp: Type)(using Context): Boolean =
1471-
isFunctionType(tp) && tp.dealias.typeSymbol.name.isErasedFunction
1470+
tp.dealias.typeSymbol.name.isErasedFunction && isFunctionType(tp)
14721471

14731472
/** A whitelist of Scala-2 classes that are known to be pure */
14741473
def isAssuredNoInits(sym: Symbol): Boolean =

compiler/src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -169,56 +169,89 @@ object NameOps {
169169
}
170170
}
171171

172-
def functionArity: Int =
173-
functionArityFor(str.Function) max
174-
functionArityFor(str.ContextFunction) max {
175-
val n =
176-
functionArityFor(str.ErasedFunction) max
177-
functionArityFor(str.ErasedContextFunction)
178-
if (n == 0) -1 else n
179-
}
172+
private def functionSuffixStart: Int =
173+
val first = name.firstPart
174+
var idx = first.length - 1
175+
if idx >= 8 && first(idx).isDigit then
176+
while
177+
idx = idx - 1
178+
idx >= 8 && first(idx).isDigit
179+
do ()
180+
if first(idx - 7) == 'F'
181+
&& first(idx - 6) == 'u'
182+
&& first(idx - 5) == 'n'
183+
&& first(idx - 4) == 'c'
184+
&& first(idx - 3) == 't'
185+
&& first(idx - 2) == 'i'
186+
&& first(idx - 1) == 'o'
187+
&& first(idx) == 'n'
188+
then idx - 7
189+
else -1
190+
else -1
191+
192+
/** The arity of a name ending in the suffix `Function{\d}`, but -1
193+
* if the number is larger than Int.MaxValue / 10.
194+
* @param suffixStart The index of the suffix
195+
*/
196+
private def funArity(suffixStart: Int): Int =
197+
inline val MaxSafeInt = MaxValue / 10
198+
val first = name.firstPart
199+
def collectDigits(acc: Int, idx: Int): Int =
200+
if idx == first.length then acc
201+
else
202+
val d = digit2int(first(idx), 10)
203+
if d < 0 || acc > MaxSafeInt then -1
204+
else collectDigits(acc * 10 + d, idx + 1)
205+
collectDigits(0, suffixStart + 8)
180206

181-
/** Is a function name, i.e one of FunctionXXL, FunctionN, ContextFunctionN for N >= 0 or ErasedFunctionN, ErasedContextFunctionN for N > 0
207+
/** name[0..suffixStart) == `str` */
208+
private def isPreceded(str: String, suffixStart: Int) =
209+
str.length == suffixStart && name.firstPart.startsWith(str)
210+
211+
/** Same as `funArity`, except that it returns -1 if the prefix
212+
* is not one of "", "Context", "Erased", "ErasedContext"
213+
*/
214+
private def checkedFunArity(suffixStart: Int) =
215+
if suffixStart == 0
216+
|| isPreceded("Context", suffixStart)
217+
|| isPreceded("Erased", suffixStart)
218+
|| isPreceded("ErasedContext", suffixStart)
219+
then funArity(suffixStart)
220+
else -1
221+
222+
/** Is a function name, i.e one of FunctionXXL, FunctionN, ContextFunctionN, ErasedFunctionN, ErasedContextFunctionN for N >= 0
182223
*/
183-
def isFunction: Boolean = (name eq tpnme.FunctionXXL) || functionArity >= 0
224+
def isFunction: Boolean =
225+
(name eq tpnme.FunctionXXL) || checkedFunArity(functionSuffixStart) >= 0
184226

185-
/** Is an context function name, i.e one of ContextFunctionN for N >= 0 or ErasedContextFunctionN for N > 0
227+
/** Is an context function name, i.e one of ContextFunctionN or ErasedContextFunctionN for N >= 0
186228
*/
187229
def isContextFunction: Boolean =
188-
functionArityFor(str.ContextFunction) >= 0 ||
189-
functionArityFor(str.ErasedContextFunction) > 0
230+
val suffixStart = functionSuffixStart
231+
(isPreceded("Context", suffixStart) || isPreceded("ErasedContext", suffixStart))
232+
&& funArity(suffixStart) >= 0
190233

191-
/** Is an erased function name, i.e. one of ErasedFunctionN, ErasedContextFunctionN for N > 0
234+
/** Is an erased function name, i.e. one of ErasedFunctionN, ErasedContextFunctionN for N >= 0
192235
*/
193236
def isErasedFunction: Boolean =
194-
functionArityFor(str.ErasedFunction) > 0 ||
195-
functionArityFor(str.ErasedContextFunction) > 0
237+
val suffixStart = functionSuffixStart
238+
(isPreceded("Erased", suffixStart) || isPreceded("ErasedContext", suffixStart))
239+
&& funArity(suffixStart) >= 0
196240

197241
/** Is a synthetic function name, i.e. one of
198242
* - FunctionN for N > 22
199243
* - ContextFunctionN for N >= 0
200-
* - ErasedFunctionN for N > 0
201-
* - ErasedContextFunctionN for N > 0
244+
* - ErasedFunctionN for N >= 0
245+
* - ErasedContextFunctionN for N >= 0
202246
*/
203247
def isSyntheticFunction: Boolean =
204-
functionArityFor(str.Function) > MaxImplementedFunctionArity ||
205-
functionArityFor(str.ContextFunction) >= 0 ||
206-
isErasedFunction
248+
val suffixStart = functionSuffixStart
249+
if suffixStart == 0 then funArity(suffixStart) > MaxImplementedFunctionArity
250+
else checkedFunArity(suffixStart) >= 0
207251

208-
/** Parsed function arity for function with some specific prefix */
209-
private def functionArityFor(prefix: String): Int =
210-
inline val MaxSafeInt = MaxValue / 10
211-
val first = name.firstPart
212-
def collectDigits(acc: Int, idx: Int): Int =
213-
if idx == first.length then acc
214-
else
215-
val d = digit2int(first(idx), 10)
216-
if d < 0 || acc > MaxSafeInt then -1
217-
else collectDigits(acc * 10 + d, idx + 1)
218-
if first.startsWith(prefix) && prefix.length < first.length then
219-
collectDigits(0, prefix.length)
220-
else
221-
-1
252+
def functionArity: Int =
253+
val suffixStart = functionSuffixStart
254+
if suffixStart >= 0 then checkedFunArity(suffixStart) else -1
222255

223256
/** The name of the generic runtime operation corresponding to an array operation */
224257
def genericArrayOp: TermName = name match {

0 commit comments

Comments
 (0)