Skip to content

Commit

Permalink
Fix scala#1543: Add synthetic FunctionN types.
Browse files Browse the repository at this point in the history
With this change it is possible to have create types for
funtions of any arity. This ensures that the compiler will
not crash when creating FunProto when typing a method
application method applications with more than MaxFunctionArity
parameters. Note that in the language the functions are still
bound to MaxFunctionArity, but this is a first small step towards
supporting any function arity.
  • Loading branch information
nicolasstucki committed Oct 3, 2016
1 parent 93d4c8c commit abfa986
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 9 deletions.
50 changes: 42 additions & 8 deletions src/dotty/tools/dotc/core/Definitions.scala
Expand Up @@ -596,19 +596,51 @@ class Definitions {
lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxAbstractFunctionArity, 0)
val AbstractFunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => AbstractFunctionType.map(_.symbol.asClass))
def AbstractFunctionClass(n: Int)(implicit ctx: Context) = AbstractFunctionClassPerRun()(ctx)(n)
lazy val FunctionType = mkArityArray("scala.Function", MaxFunctionArity, 0)
def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => FunctionType.map(_.symbol.asClass))

private lazy val functionType = mkArityArray("scala.Function", MaxFunctionArity, 0)
def FunctionType(arity: Int): TypeRef = {
if (arity <= MaxFunctionArity) functionType(arity)
else SyntheticFunctionType(arity)
}
def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => functionType.map(_.symbol.asClass))
def FunctionClass(n: Int)(implicit ctx: Context) = FunctionClassPerRun()(ctx)(n)
lazy val Function0_applyR = FunctionType(0).symbol.requiredMethodRef(nme.apply)
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol


private lazy val syntheticFunctionTypeMap = mutable.Map.empty[Int, TypeRef]
private def SyntheticFunctionType(arity: Int): TypeRef = {
assert(MaxFunctionArity < arity)
if (syntheticFunctionTypeMap.contains(arity)) syntheticFunctionTypeMap(arity)
else {
val cls = mkSyntheticFunction(arity)
syntheticCoreClasses.add(cls)
val tpe = cls.typeRef
syntheticFunctionTypeMap.put(arity, tpe)
tpe
}
}
private def mkSyntheticFunction(i: Int): ClassSymbol = {
val decls = newScope
val cls = newCompleteClassSymbol(ScalaPackageClass, tpnme.FunctionN(i), Trait, List(AnyRefType), decls)
def newTypeParam(name: TypeName, flags: FlagSet, bounds: TypeBounds) =
newSymbol(cls, name, flags | ClassTypeParamCreationFlags, bounds)

val vParamNames = (0 until i).map(j => s"i$j".toTermName).toList
val tParamSyms = (0 until i).map(j => newTypeParam(s"T$j".toTypeName, Contravariant, TypeBounds.empty)).toList
val returnTParamSym = newTypeParam("R".toTypeName, Covariant, TypeBounds.empty)
val applyMethod =
newMethod(cls, nme.apply, MethodType(vParamNames, tParamSyms.map(_.typeRef), returnTParamSym.typeRef), Deferred)

tParamSyms.foreach(decls.enter)
decls.enter(returnTParamSym)
decls.enter(applyMethod)
completeClass(cls)
}

lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)

private lazy val FunctionTypes: Set[TypeRef] = FunctionType.toSet
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet

/** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
def scalaClassName(cls: Symbol)(implicit ctx: Context): TypeName =
if (cls.isClass && cls.owner == ScalaPackageClass) cls.asClass.name else EmptyTypeName
Expand Down Expand Up @@ -753,7 +785,7 @@ class Definitions {
// ----- Initialization ---------------------------------------------------

/** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
lazy val syntheticCoreClasses = List(
private lazy val syntheticCoreClasses = mutable.Set[Symbol](
AnyClass,
AnyRefAlias,
RepeatedParamClass,
Expand All @@ -766,8 +798,10 @@ class Definitions {
EmptyPackageVal,
OpsPackageClass)

def isSyntheticCoreClass(sym: ClassSymbol) = syntheticCoreClasses(sym)

/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)
private lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)

private[this] var _isInitialized = false
private def isInitialized = _isInitialized
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/StdNames.scala
Expand Up @@ -191,6 +191,8 @@ object StdNames {
final val Throwable: N = "Throwable"
final val Tuple: N = "Tuple"

final def FunctionN(arity: Int): N = Function + arity.toString

final val ClassfileAnnotation: N = "ClassfileAnnotation"
final val ClassManifest: N = "ClassManifest"
final val Enum: N = "Enum"
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/TreeChecker.scala
Expand Up @@ -84,7 +84,7 @@ class TreeChecker extends Phase with SymTransformer {
val sym = symd.symbol

if (sym.isClass && !sym.isAbsent) {
val validSuperclass = sym.isPrimitiveValueClass || defn.syntheticCoreClasses.contains(sym) ||
val validSuperclass = sym.isPrimitiveValueClass || defn.isSyntheticCoreClass(sym.asClass) ||
(sym eq defn.ObjectClass) || (sym is NoSuperClass) || (sym.asClass.superClass.exists)
if (!validSuperclass)
printError(s"$sym has no superclass set")
Expand Down
9 changes: 9 additions & 0 deletions tests/neg/syntheticFunctions.scala
@@ -0,0 +1,9 @@

object Foo {
new Function0
new Function22
new Function30
new Function31 // error: not found: type Function31
new Function100000 // error: not found: type Function100000
new `Function-1` // error : not found: type Function-1
}
31 changes: 31 additions & 0 deletions tests/run/i1543.check
@@ -0,0 +1,31 @@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
41 changes: 41 additions & 0 deletions tests/run/i1543.scala
@@ -0,0 +1,41 @@

object Test extends dotty.runtime.LegacyApp {
new Bar().foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)
}


class Bar {
def foo(p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, p7: Int, p8: Int, p9: Int, p10: Int, p11: Int, p12: Int, p13: Int, p14: Int, p15: Int, p16: Int, p17: Int, p18: Int, p19: Int, p20: Int, p21: Int, p22: Int, p23: Int, p24: Int, p25: Int, p26: Int, p27: Int, p28: Int, p29: Int, p30: Int, p31: Int): Unit = {
println(p1)
println(p2)
println(p3)
println(p4)
println(p5)
println(p6)
println(p7)
println(p8)
println(p9)
println(p10)
println(p11)
println(p12)
println(p13)
println(p14)
println(p15)
println(p16)
println(p17)
println(p18)
println(p19)
println(p20)
println(p21)
println(p22)
println(p23)
println(p24)
println(p25)
println(p26)
println(p27)
println(p28)
println(p29)
println(p30)
println(p31)
}
}

0 comments on commit abfa986

Please sign in to comment.