Skip to content

Commit 93a2d06

Browse files
committed
Add checks for synthetic functions and erased functions.
* Add `isSyntheticFunction` checks for synthetic functions such as FuntionN for N > 22 and ImplicitFunctionN for N >= 0. * Add `erasedFunctionClass` to get the erased verion of synthetic functions. * Change the semantics of `isFunctionClass` to return true if it is any kind of FunctionN or ImplicitFunctionN.
1 parent b297832 commit 93a2d06

File tree

7 files changed

+111
-52
lines changed

7 files changed

+111
-52
lines changed

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

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -635,13 +635,9 @@ class Definitions {
635635
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
636636
def unapply(ft: Type)(implicit ctx: Context) = {
637637
val tsym = ft.typeSymbol
638-
val isImplicitFun = isImplicitFunctionClass(tsym)
639-
if (isImplicitFun || isFunctionClass(tsym)) {
640-
val targs = ft.argInfos
641-
val numArgs = targs.length - 1
642-
if (numArgs >= 0 && FunctionType(numArgs, isImplicitFun).symbol == tsym)
643-
Some(targs.init, targs.last, isImplicitFun)
644-
else None
638+
if (isFunctionClass(tsym)) {
639+
val targs = ft.dealias.argInfos
640+
Some(targs.init, targs.last, tsym.name.isImplicitFunction)
645641
}
646642
else None
647643
}
@@ -694,20 +690,17 @@ class Definitions {
694690
lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
695691
lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)
696692

697-
def FunctionClass(n: Int)(implicit ctx: Context) =
698-
if (n < MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
693+
def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context) =
694+
if (isImplicit) ctx.requiredClass("scala.ImplicitFunction" + n.toString)
695+
else if (n <= MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
699696
else ctx.requiredClass("scala.Function" + n.toString)
700697

701698
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
702699
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
703700

704-
def ImplicitFunctionClass(n: Int)(implicit ctx: Context) =
705-
ctx.requiredClass("scala.ImplicitFunction" + n.toString)
706-
707701
def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
708-
if (isImplicit && !ctx.erasedTypes) ImplicitFunctionClass(n).typeRef
709-
else if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n)
710-
else FunctionClass(n).typeRef
702+
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n)
703+
else FunctionClass(n, isImplicit).typeRef
711704

712705
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
713706
private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet
@@ -731,14 +724,61 @@ class Definitions {
731724
def isBottomType(tp: Type) =
732725
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
733726

734-
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
735-
def isImplicitFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.ImplicitFunction)
736-
/** Is a class that will be erased to FunctionXXL */
737-
def isXXLFunctionClass(cls: Symbol) = cls.name.functionArity > MaxImplementedFunctionArity
727+
/** Is a function class.
728+
* - FunctionN for N >= 0
729+
* - ImplicitFunctionN for N >= 0
730+
*/
731+
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction
732+
733+
/** Is an implicit function class.
734+
* - ImplicitFunctionN for N >= 0
735+
*/
736+
def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction
737+
738+
/** Is a class that will be erased to FunctionXXL
739+
* - FunctionN for N >= 22
740+
* - ImplicitFunctionN for N >= 22
741+
*/
742+
def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity
743+
744+
/** Is a synthetic function class
745+
* - FunctionN for N > 22
746+
* - ImplicitFunctionN for N >= 0
747+
*/
748+
def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction
749+
738750
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
739751
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
740752
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
741753

754+
/** Returns the erased class of the function class `cls`
755+
* - FunctionN for N > 22 becomes FunctionXXL
756+
* - FunctionN for 22 > N >= 0 remains as FunctionN
757+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
758+
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
759+
* - anything else becomes a NoSymbol
760+
*/
761+
def erasedFunctionClass(cls: Symbol): Symbol = {
762+
val arity = scalaClassName(cls).functionArity
763+
if (arity > 22) defn.FunctionXXLClass
764+
else if (arity >= 0) defn.FunctionClass(arity)
765+
else NoSymbol
766+
}
767+
768+
/** Returns the erased type of the function class `cls`
769+
* - FunctionN for N > 22 becomes FunctionXXL
770+
* - FunctionN for 22 > N >= 0 remains as FunctionN
771+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
772+
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
773+
* - anything else becomes a NoType
774+
*/
775+
def erasedFunctionType(cls: Symbol): Type = {
776+
val arity = scalaClassName(cls).functionArity
777+
if (arity > 22) defn.FunctionXXLType
778+
else if (arity >= 0) defn.FunctionType(arity)
779+
else NoType
780+
}
781+
742782
val predefClassNames: Set[Name] =
743783
Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName)
744784

@@ -809,16 +849,13 @@ class Definitions {
809849
def isFunctionType(tp: Type)(implicit ctx: Context) = {
810850
val arity = functionArity(tp)
811851
val sym = tp.dealias.typeSymbol
812-
arity >= 0 && (
813-
isFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = false).typeSymbol) ||
814-
isImplicitFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = true).typeSymbol)
815-
)
852+
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
816853
}
817854

818855
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
819856

820857
def isImplicitFunctionType(tp: Type)(implicit ctx: Context) =
821-
isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction)
858+
isFunctionType(tp) && tp.dealias.typeSymbol.name.isImplicitFunction
822859

823860
// ----- primitive value class machinery ------------------------------------------
824861

@@ -892,9 +929,6 @@ class Definitions {
892929

893930
// ----- Initialization ---------------------------------------------------
894931

895-
private def maxImplemented(name: Name) =
896-
if (name `startsWith` tpnme.Function) MaxImplementedFunctionArity else 0
897-
898932
/** Give the scala package a scope where a FunctionN trait is automatically
899933
* added when someone looks for it.
900934
*/
@@ -904,7 +938,7 @@ class Definitions {
904938
val newDecls = new MutableScope(oldDecls) {
905939
override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
906940
val res = super.lookupEntry(name)
907-
if (res == null && name.isTypeName && name.functionArity > maxImplemented(name))
941+
if (res == null && name.isTypeName && name.isSyntheticFunction)
908942
newScopeEntry(newFunctionNTrait(name.asTypeName))
909943
else res
910944
}

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

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Names._, StdNames._, Contexts._, Symbols._, Flags._
88
import Decorators.StringDecorator
99
import util.{Chars, NameTransformer}
1010
import Chars.isOperatorPart
11+
import Definitions._
1112

1213
object NameOps {
1314

@@ -231,13 +232,43 @@ object NameOps {
231232
}
232233
}
233234

234-
def functionArity: Int = {
235-
def test(prefix: Name): Int =
236-
if (name.startsWith(prefix))
237-
try name.drop(prefix.length).toString.toInt
238-
catch { case ex: NumberFormatException => -1 }
239-
else -1
240-
test(tpnme.Function) max test(tpnme.ImplicitFunction)
235+
/** Is a synthetic function name
236+
* - N for FunctionN
237+
* - N for ImplicitFunctionN
238+
* - (-1) otherwise
239+
*/
240+
def functionArity: Int =
241+
functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction)
242+
243+
/** Is a function name
244+
* - FunctionN for N >= 0
245+
* - ImplicitFunctionN for N >= 0
246+
* - false otherwise
247+
*/
248+
def isFunction: Boolean = functionArity >= 0
249+
250+
/** Is a implicit function name
251+
* - ImplicitFunctionN for N >= 0
252+
* - false otherwise
253+
*/
254+
def isImplicitFunction: Boolean = functionArityFor(tpnme.ImplicitFunction) >= 0
255+
256+
/** Is a synthetic function name
257+
* - FunctionN for N > 22
258+
* - ImplicitFunctionN for N >= 0
259+
* - false otherwise
260+
*/
261+
def isSyntheticFunction: Boolean = {
262+
functionArityFor(tpnme.Function) > MaxImplementedFunctionArity ||
263+
functionArityFor(tpnme.ImplicitFunction) >= 0
264+
}
265+
266+
/** Parsed function arity for function with some specific prefix */
267+
private def functionArityFor(prefix: Name): Int = {
268+
if (name.startsWith(prefix))
269+
try name.toString.substring(prefix.length).toInt
270+
catch { case _: NumberFormatException => -1 }
271+
else -1
241272
}
242273

243274
/** The name of the generic runtime operation corresponding to an array operation */

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ object TypeErasure {
4444
val sym = tp.symbol
4545
sym.isClass &&
4646
sym != defn.AnyClass && sym != defn.ArrayClass &&
47-
!defn.isXXLFunctionClass(sym) && !defn.isImplicitFunctionClass(sym)
47+
!defn.isSyntheticFunctionClass(sym)
4848
case _: TermRef =>
4949
true
5050
case JavaArrayType(elem) =>
@@ -358,8 +358,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
358358
if (!sym.isClass) this(tp.info)
359359
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
360360
else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type.
361-
else if (defn.isXXLFunctionClass(sym)) defn.FunctionXXLType
362-
else if (defn.isImplicitFunctionClass(sym)) apply(defn.FunctionType(sym.name.functionArity))
361+
else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym)
363362
else eraseNormalClassRef(tp)
364363
case tp: RefinedType =>
365364
val parent = tp.parent
@@ -370,7 +369,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
370369
case SuperType(thistpe, supertpe) =>
371370
SuperType(this(thistpe), this(supertpe))
372371
case ExprType(rt) =>
373-
defn.FunctionClass(0).typeRef
372+
defn.FunctionType(0)
374373
case AndType(tp1, tp2) =>
375374
erasedGlb(this(tp1), this(tp2), isJava)
376375
case OrType(tp1, tp2) =>
@@ -496,8 +495,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
496495
val erasedVCRef = eraseDerivedValueClassRef(tp)
497496
if (erasedVCRef.exists) return sigName(erasedVCRef)
498497
}
499-
if (defn.isImplicitFunctionClass(sym))
500-
sigName(defn.FunctionType(sym.name.functionArity))
498+
if (defn.isSyntheticFunctionClass(sym))
499+
sigName(defn.erasedFunctionType(sym))
501500
else
502501
normalizeClass(sym.asClass).fullName.asTypeName
503502
case defn.ArrayOf(elem) =>

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
126126
case AppliedType(tycon, args) =>
127127
val cls = tycon.typeSymbol
128128
if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*"
129-
if (defn.isFunctionClass(cls)) return toTextFunction(args, isImplicit = false)
130-
if (defn.isImplicitFunctionClass(cls)) return toTextFunction(args, isImplicit = true)
129+
if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction)
131130
if (defn.isTupleClass(cls)) return toTextTuple(args)
132131
return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close
133132
case tp: TypeRef =>

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,10 +349,8 @@ object Erasure extends TypeTestsCasts{
349349
if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) {
350350
assert(sym.isConstructor, s"${sym.showLocated}")
351351
defn.ObjectClass
352-
} else if (defn.isXXLFunctionClass(owner))
353-
defn.FunctionXXLClass
354-
else if (defn.isImplicitFunctionClass(owner))
355-
recur(defn.FunctionClass(owner.name.functionArity))
352+
} else if (defn.isSyntheticFunctionClass(owner))
353+
defn.erasedFunctionClass(owner)
356354
else
357355
owner
358356
recur(sym.owner)

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class TreeChecker extends Phase with SymTransformer {
8181
val sym = symd.symbol
8282

8383
if (sym.isClass && !sym.isAbsent) {
84-
val validSuperclass = sym.isPrimitiveValueClass || defn.syntheticCoreClasses.contains(sym) ||
84+
val validSuperclass = sym.isPrimitiveValueClass || defn.syntheticCoreClasses.contains(sym) ||
8585
(sym eq defn.ObjectClass) || (sym is NoSuperClass) || (sym.asClass.superClass.exists)
8686
if (!validSuperclass)
8787
printError(s"$sym has no superclass set")

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -664,9 +664,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
664664
def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") {
665665
val untpd.Function(args, body) = tree
666666
if (ctx.mode is Mode.Type) {
667-
val funCls =
668-
if (tree.isInstanceOf[untpd.ImplicitFunction]) defn.ImplicitFunctionClass(args.length)
669-
else defn.FunctionClass(args.length)
667+
val funCls = defn.FunctionClass(args.length, tree.isInstanceOf[untpd.ImplicitFunction])
670668
typed(cpy.AppliedTypeTree(tree)(
671669
untpd.TypeTree(funCls.typeRef), args :+ body), pt)
672670
}
@@ -1937,7 +1935,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19371935
!untpd.isImplicitClosure(tree) &&
19381936
!isApplyProto(pt) &&
19391937
!ctx.isAfterTyper) {
1940-
typr.println("insert apply on implicit $tree")
1938+
typr.println(i"insert apply on implicit $tree")
19411939
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
19421940
}
19431941
else if (ctx.mode is Mode.Pattern) {

0 commit comments

Comments
 (0)