From abfa98675a67b5e1d3b1ed707bbc5af46047cc43 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 30 Sep 2016 11:34:43 +0200 Subject: [PATCH] Fix #1543: Add synthetic FunctionN types. 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. --- src/dotty/tools/dotc/core/Definitions.scala | 50 ++++++++++++++++--- src/dotty/tools/dotc/core/StdNames.scala | 2 + .../tools/dotc/transform/TreeChecker.scala | 2 +- tests/neg/syntheticFunctions.scala | 9 ++++ tests/run/i1543.check | 31 ++++++++++++ tests/run/i1543.scala | 41 +++++++++++++++ 6 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 tests/neg/syntheticFunctions.scala create mode 100644 tests/run/i1543.check create mode 100644 tests/run/i1543.scala diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index cb83fda0414a..61a62dc4b5c6 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -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 @@ -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, @@ -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 diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index f47ab17449e9..6669fa0f5234 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -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" diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index e7342aec9c02..d68e6a9483d1 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -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") diff --git a/tests/neg/syntheticFunctions.scala b/tests/neg/syntheticFunctions.scala new file mode 100644 index 000000000000..8e1da1e3d14e --- /dev/null +++ b/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 +} diff --git a/tests/run/i1543.check b/tests/run/i1543.check new file mode 100644 index 000000000000..10adcaf83eb4 --- /dev/null +++ b/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 diff --git a/tests/run/i1543.scala b/tests/run/i1543.scala new file mode 100644 index 000000000000..268bc717bb59 --- /dev/null +++ b/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) + } +}