diff options
Diffstat (limited to 'compiler/src/dotty/tools')
7 files changed, 225 insertions, 97 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index a7c449947..397382c2f 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -140,7 +140,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma val externalEqualsNumChar: Symbol = NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private val externalEqualsNumObject: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject) val externalEquals: Symbol = defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol - val MaxFunctionArity: Int = Definitions.MaxFunctionArity + val MaxFunctionArity: Int = Definitions.MaxImplementedFunctionArity val FunctionClass: Array[Symbol] = defn.FunctionClassPerRun() val AbstractFunctionClass: Array[Symbol] = defn.AbstractFunctionClassPerRun() val PartialFunctionClass: Symbol = defn.PartialFunctionClass diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index c5ccab261..e4e5761b2 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -12,13 +12,20 @@ import collection.mutable import scala.reflect.api.{ Universe => ApiUniverse } object Definitions { - val MaxTupleArity, MaxAbstractFunctionArity = 22 - val MaxFunctionArity = 30 - // Awaiting a definite solution that drops the limit altogether, 30 gives a safety - // margin over the previous 22, so that treecopiers in miniphases are allowed to - // temporarily create larger closures. This is needed in lambda lift where large closures - // are first formed by treecopiers before they are split apart into parameters and - // environment in the lambdalift transform itself. + + /** The maximum number of elements in a tuple or product. + * This should be removed once we go to hlists. + */ + val MaxTupleArity = 22 + + /** The maximum arity N of a function type that's implemented + * as a trait `scala.FunctionN`. Functions of higher arity are possible, + * but are mapped in erasure to functions taking a single parameter of type + * Object[]. + * The limit 22 is chosen for Scala2x interop. It could be something + * else without affecting the set of programs that can be compiled. + */ + val MaxImplementedFunctionArity = 22 } /** A class defining symbols and types of standard definitions @@ -45,32 +52,29 @@ class Definitions { ctx.newSymbol(owner, name, flags | Permanent, info) private def newClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, infoFn: ClassSymbol => Type) = - ctx.newClassSymbol(owner, name, flags | Permanent, infoFn).entered + ctx.newClassSymbol(owner, name, flags | Permanent, infoFn) - private def newCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = + private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered - private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = - completeClass(newCompleteClassSymbol(ScalaPackageClass, name, flags, parents)) - - private def newTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = + private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) - private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - newTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) + private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = + enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) - private def newSyntheticTypeParam(cls: ClassSymbol, scope: MutableScope, paramFlags: FlagSet, suffix: String = "T0") = - newTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) + private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") = + enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) // NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only // implemented in Dotty and not in Scala 2. // See <http://docs.scala-lang.org/sips/pending/repeated-byname.html>. - private def specialPolyClass(name: TypeName, paramFlags: FlagSet, parentConstrs: => Seq[Type]): ClassSymbol = { + private def enterSpecialPolyClass(name: TypeName, paramFlags: FlagSet, parentConstrs: => Seq[Type]): ClassSymbol = { val completer = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val paramDecls = newScope - val typeParam = newSyntheticTypeParam(cls, paramDecls, paramFlags) + val typeParam = enterSyntheticTypeParam(cls, paramFlags, paramDecls) def instantiate(tpe: Type) = if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef) else tpe @@ -79,31 +83,54 @@ class Definitions { denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parentRefs, paramDecls) } } - newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer) + newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered + } + + /** The trait FunctionN, for some N */ + private def newFunctionNTrait(n: Int) = { + val completer = new LazyType { + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { + val cls = denot.asClass.classSymbol + val decls = newScope + val argParams = + for (i <- List.range(0, n)) yield + enterTypeParam(cls, s"T$i".toTypeName, Contravariant, decls) + val resParam = enterTypeParam(cls, s"R".toTypeName, Covariant, decls) + val applyMeth = + decls.enter( + newMethod(cls, nme.apply, + MethodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) + denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) + } + } + newClassSymbol(ScalaPackageClass, s"Function$n".toTypeName, Trait, completer) } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = - newSymbol(cls, name.encode, flags | Method, info).entered.asTerm + newSymbol(cls, name.encode, flags | Method, info).asTerm - private def newAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = { + private def enterMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = + newMethod(cls, name, info, flags).entered + + private def enterAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = { val sym = newSymbol(ScalaPackageClass, name, flags, TypeAlias(tpe)) ScalaPackageClass.currentPackageDecls.enter(sym) sym } - private def newPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, + private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) val tparamBounds = tparamNames map (_ => TypeBounds.empty) val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn) - newMethod(cls, name, ptype, flags) + enterMethod(cls, name, ptype, flags) } - private def newT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, resultTypeFn, flags) + private def enterT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = + enterPolyMethod(cls, name, 1, resultTypeFn, flags) - private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) + private def enterT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = + enterPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[TypeRef] = { val arr = new Array[TypeRef](arity + 1) @@ -172,20 +199,20 @@ class Definitions { * def getClass: java.lang.Class[T] = ??? * } */ - lazy val AnyClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)) + lazy val AnyClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)) def AnyType = AnyClass.typeRef - lazy val AnyValClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) + lazy val AnyValClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) def AnyValType = AnyValClass.typeRef - lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) - lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) - lazy val Any_equals = newMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) - lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) - lazy val Any_toString = newMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) - lazy val Any_## = newMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) - lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) - lazy val Any_isInstanceOf = newT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) - lazy val Any_asInstanceOf = newT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) + lazy val Any_== = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) + lazy val Any_!= = enterMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) + lazy val Any_equals = enterMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) + lazy val Any_hashCode = enterMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) + lazy val Any_toString = enterMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) + lazy val Any_## = enterMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) + lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) + lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) + lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf) @@ -205,37 +232,37 @@ class Definitions { } def ObjectType = ObjectClass.typeRef - lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType) + lazy val AnyRefAlias: TypeSymbol = enterAliasType(tpnme.AnyRef, ObjectType) def AnyRefType = AnyRefAlias.typeRef - lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) - lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) - lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1, + lazy val Object_eq = enterMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) + lazy val Object_ne = enterMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) + lazy val Object_synchronized = enterPolyMethod(ObjectClass, nme.synchronized_, 1, pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) - lazy val Object_clone = newMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) - lazy val Object_finalize = newMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) - lazy val Object_notify = newMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) - lazy val Object_notifyAll = newMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType)) - lazy val Object_wait = newMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType)) - lazy val Object_waitL = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType)) - lazy val Object_waitLI = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType)) + lazy val Object_clone = enterMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) + lazy val Object_finalize = enterMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) + lazy val Object_notify = enterMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) + lazy val Object_notifyAll = enterMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType)) + lazy val Object_wait = enterMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType)) + lazy val Object_waitL = enterMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType)) + lazy val Object_waitLI = enterMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType)) def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone, Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI) /** Dummy method needed by elimByName */ - lazy val dummyApply = newPolyMethod( + lazy val dummyApply = enterPolyMethod( OpsPackageClass, nme.dummyApply, 1, pt => MethodType(List(FunctionOf(Nil, PolyParam(pt, 0))), PolyParam(pt, 0))) /** Method representing a throw */ - lazy val throwMethod = newMethod(OpsPackageClass, nme.THROWkw, + lazy val throwMethod = enterMethod(OpsPackageClass, nme.THROWkw, MethodType(List(ThrowableType), NothingType)) - lazy val NothingClass: ClassSymbol = newCompleteClassSymbol( + lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol( ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef)) def NothingType = NothingClass.typeRef - lazy val NullClass: ClassSymbol = newCompleteClassSymbol( + lazy val NullClass: ClassSymbol = enterCompleteClassSymbol( ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef)) def NullType = NullClass.typeRef @@ -281,7 +308,7 @@ class Definitions { lazy val SingletonClass: ClassSymbol = // needed as a synthetic class because Scala 2.x refers to it in classfiles // but does not define it as an explicit class. - newCompleteClassSymbol( + enterCompleteClassSymbol( ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final, List(AnyClass.typeRef), EmptyScope) @@ -387,17 +414,17 @@ class Definitions { lazy val BoxedDoubleModule = ctx.requiredModule("java.lang.Double") lazy val BoxedUnitModule = ctx.requiredModule("java.lang.Void") - lazy val ByNameParamClass2x = specialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, Seq(AnyType)) - lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType)) + lazy val ByNameParamClass2x = enterSpecialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, Seq(AnyType)) + lazy val EqualsPatternClass = enterSpecialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType)) - lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType)) + lazy val RepeatedParamClass = enterSpecialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType)) // fundamental classes lazy val StringClass = ctx.requiredClass("java.lang.String") def StringType: Type = StringClass.typeRef lazy val StringModule = StringClass.linkedClass - lazy val String_+ = newMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final) + lazy val String_+ = enterMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final) lazy val String_valueOf_Object = StringModule.info.member(nme.valueOf).suchThat(_.info.firstParamTypes match { case List(pt) => (pt isRef AnyClass) || (pt isRef ObjectClass) case _ => false @@ -431,6 +458,9 @@ class Definitions { def PartialFunctionClass(implicit ctx: Context) = PartialFunctionType.symbol.asClass lazy val AbstractPartialFunctionType: TypeRef = ctx.requiredClassRef("scala.runtime.AbstractPartialFunction") def AbstractPartialFunctionClass(implicit ctx: Context) = AbstractPartialFunctionType.symbol.asClass + lazy val FunctionXXLType: TypeRef = ctx.requiredClassRef("scala.FunctionXXL") + def FunctionXXLClass(implicit ctx: Context) = FunctionXXLType.symbol.asClass + lazy val SymbolType: TypeRef = ctx.requiredClassRef("scala.Symbol") def SymbolClass(implicit ctx: Context) = SymbolType.symbol.asClass lazy val DynamicType: TypeRef = ctx.requiredClassRef("scala.Dynamic") @@ -562,14 +592,15 @@ class Definitions { object FunctionOf { def apply(args: List[Type], resultType: Type)(implicit ctx: Context) = FunctionType(args.length).appliedTo(args ::: resultType :: Nil) - def unapply(ft: Type)(implicit ctx: Context)/*: Option[(List[Type], Type)]*/ = { - // -language:keepUnions difference: unapply needs result type because inferred type - // is Some[(List[Type], Type)] | None, which is not a legal unapply type. + def unapply(ft: Type)(implicit ctx: Context) = { val tsym = ft.typeSymbol - lazy val targs = ft.argInfos - val numArgs = targs.length - 1 - if (numArgs >= 0 && numArgs <= MaxFunctionArity && - (FunctionType(numArgs).symbol == tsym)) Some(targs.init, targs.last) + if (isFunctionClass(tsym)) { + lazy val targs = ft.argInfos + val numArgs = targs.length - 1 + if (numArgs >= 0 && FunctionType(numArgs).symbol == tsym) + Some(targs.init, targs.last) + else None + } else None } } @@ -612,19 +643,26 @@ class Definitions { // ----- Symbol sets --------------------------------------------------- - lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxAbstractFunctionArity, 0) + lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 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)) - 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 ImplementedFunctionType = mkArityArray("scala.Function", MaxImplementedFunctionArity, 0) + def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => ImplementedFunctionType.map(_.symbol.asClass)) lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2) lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0) - private lazy val FunctionTypes: Set[TypeRef] = FunctionType.toSet + def FunctionClass(n: Int)(implicit ctx: Context) = + if (n < MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n) + else ctx.requiredClass("scala.Function" + n.toString) + + lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply) + def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol + + def FunctionType(n: Int)(implicit ctx: Context): TypeRef = + if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n) + else FunctionClass(n).typeRef + private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet @@ -646,6 +684,8 @@ class Definitions { tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) + def isUnimplementedFunctionClass(cls: Symbol) = + isFunctionClass(cls) && cls.name.functionArity > MaxImplementedFunctionArity def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) @@ -695,10 +735,11 @@ class Definitions { def isProductSubType(tp: Type)(implicit ctx: Context) = (tp derivesFrom ProductType.symbol) && tp.baseClasses.exists(isProductClass) - def isFunctionType(tp: Type)(implicit ctx: Context) = { - val arity = functionArity(tp) - 0 <= arity && arity <= MaxFunctionArity && (tp isRef FunctionType(arity).symbol) - } + def isFunctionType(tp: Type)(implicit ctx: Context) = + isFunctionClass(tp.dealias.typeSymbol) && { + val arity = functionArity(tp) + arity >= 0 && tp.isRef(FunctionType(functionArity(tp)).typeSymbol) + } def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 @@ -774,6 +815,23 @@ class Definitions { // ----- Initialization --------------------------------------------------- + /** Give the scala package a scope where a FunctionN trait is automatically + * added when someone looks for it. + */ + private def makeScalaSpecial()(implicit ctx: Context) = { + val oldInfo = ScalaPackageClass.classInfo + val oldDecls = oldInfo.decls + val newDecls = new MutableScope(oldDecls) { + override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { + val res = super.lookupEntry(name) + if (res == null && name.functionArity > 0) + newScopeEntry(newFunctionNTrait(name.functionArity)) + else res + } + } + ScalaPackageClass.info = oldInfo.derivedClassInfo(decls = newDecls) + } + /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticScalaClasses = List( AnyClass, @@ -801,6 +859,8 @@ class Definitions { def init()(implicit ctx: Context) = { this.ctx = ctx if (!_isInitialized) { + makeScalaSpecial() + // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 4c7f5b0a9..7a4fc0512 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -229,6 +229,12 @@ object NameOps { } } + def functionArity: Int = + if (name.startsWith(tpnme.Function)) + try name.drop(tpnme.Function.length).toString.toInt + catch { case ex: NumberFormatException => -1 } + else -1 + /** The name of the generic runtime operation corresponding to an array operation */ def genericArrayOp: TermName = name match { case nme.apply => nme.array_apply diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index 3daa8117e..6090079e5 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -309,7 +309,7 @@ object Scopes { /** Lookup a symbol entry matching given name. */ - override final def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { + override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { var e: ScopeEntry = null if (hashTable ne null) { e = hashTable(name.hashCode & (hashTable.length - 1)) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index c2a14b36f..741ff8b1f 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -265,6 +265,7 @@ object StdNames { val THIS: N = "_$this" val TRAIT_CONSTRUCTOR: N = "$init$" val U2EVT: N = "u2evt$" + val ALLARGS: N = "$allArgs" final val Nil: N = "Nil" final val Predef: N = "Predef" diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index abbacee49..57397a8bc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -7,6 +7,7 @@ import Uniques.unique import dotc.transform.ExplicitOuter._ import dotc.transform.ValueClasses._ import util.DotClass +import Definitions.MaxImplementedFunctionArity /** Erased types are: * @@ -38,7 +39,10 @@ object TypeErasure { case _: ErasedValueType => true case tp: TypeRef => - tp.symbol.isClass && tp.symbol != defn.AnyClass && tp.symbol != defn.ArrayClass + val sym = tp.symbol + sym.isClass && + sym != defn.AnyClass && sym != defn.ArrayClass && + !defn.isUnimplementedFunctionClass(sym) case _: TermRef => true case JavaArrayType(elem) => @@ -176,8 +180,13 @@ object TypeErasure { else if (sym.isAbstractType) TypeAlias(WildcardType) else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) else erase.eraseInfo(tp, sym)(erasureCtx) match { - case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => - MethodType(Nil, defn.BoxedUnitType) + case einfo: MethodType => + if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass)) + MethodType(Nil, defn.BoxedUnitType) + else if (sym.isAnonymousFunction && einfo.paramTypes.length > MaxImplementedFunctionArity) + MethodType(nme.ALLARGS :: Nil, JavaArrayType(defn.ObjectType) :: Nil, einfo.resultType) + else + einfo case einfo => einfo } @@ -317,6 +326,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean * - For a term ref p.x, the type <noprefix> # x. * - For a typeref scala.Any, scala.AnyVal or scala.Singleton: |java.lang.Object| * - For a typeref scala.Unit, |scala.runtime.BoxedUnit|. + * - For a typeref scala.FunctionN, where N > MaxImplementedFunctionArity, scala.FunctionXXL * - For a typeref P.C where C refers to a class, <noprefix> # C. * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. * - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound. @@ -345,6 +355,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (!sym.isClass) this(tp.info) else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. + else if (defn.isUnimplementedFunctionClass(sym)) defn.FunctionXXLType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 069176111..d1f5bd532 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -13,6 +13,7 @@ import core.StdNames._ import core.NameOps._ import core.Decorators._ import core.Constants._ +import core.Definitions._ import typer.NoChecking import typer.ProtoTypes._ import typer.ErrorReporting._ @@ -36,9 +37,17 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: SymDenotation => + def isCompacted(sym: Symbol) = + sym.isAnonymousFunction && { + sym.info(ctx.withPhase(ctx.phase.next)) match { + case MethodType(nme.ALLARGS :: Nil, _) => true + case _ => false + } + } + assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}") if (ref.symbol eq defn.ObjectClass) { - // Aftre erasure, all former Any members are now Object members + // After erasure, all former Any members are now Object members val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info val extendedScope = decls.cloneScope for (decl <- defn.AnyClass.classInfo.decls) @@ -59,7 +68,10 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => val oldInfo = ref.info val newInfo = transformInfo(ref.symbol, oldInfo) val oldFlags = ref.flags - val newFlags = ref.flags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading + val newFlags = + if (oldSymbol.is(Flags.TermParam) && isCompacted(oldSymbol.owner)) oldFlags &~ Flags.Param + else oldFlags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading + // TODO: define derivedSymDenotation? if ((oldSymbol eq newSymbol) && (oldOwner eq newOwner) && (oldInfo eq newInfo) && (oldFlags == newFlags)) ref else { @@ -331,8 +343,23 @@ object Erasure extends TypeTestsCasts{ * e.m -> e.[]m if `m` is an array operation other than `clone`. */ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - val sym = tree.symbol - assert(sym.exists, tree.show) + + def mapOwner(sym: Symbol): Symbol = { + val owner = sym.owner + if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) { + assert(sym.isConstructor, s"${sym.showLocated}") + defn.ObjectClass + } + else if (defn.isUnimplementedFunctionClass(owner)) + defn.FunctionXXLClass + else + owner + } + + var sym = tree.symbol + val owner = mapOwner(sym) + if (owner ne sym.owner) sym = owner.info.decl(sym.name).symbol + assert(sym.exists, owner) def select(qual: Tree, sym: Symbol): Tree = { val name = tree.typeOpt match { @@ -366,11 +393,7 @@ object Erasure extends TypeTestsCasts{ def recur(qual: Tree): Tree = { val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType val symIsPrimitive = sym.owner.isPrimitiveValueClass - if ((sym.owner eq defn.AnyClass) || (sym.owner eq defn.AnyValClass)) { - assert(sym.isConstructor, s"${sym.showLocated}") - select(qual, defn.ObjectClass.info.decl(sym.name).symbol) - } - else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType) + if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType) recur(box(qual)) else if (!qualIsPrimitive && symIsPrimitive) recur(unbox(qual, sym.owner.typeRef)) @@ -423,6 +446,9 @@ object Erasure extends TypeTestsCasts{ } } + /** Besides normal typing, this method collects all arguments + * to a compacted function into a single argument of array type. + */ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree if (fun.symbol == defn.dummyApply) @@ -434,7 +460,13 @@ object Erasure extends TypeTestsCasts{ fun1.tpe.widen match { case mt: MethodType => val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased - val args1 = (outers ::: args ++ protoArgs(pt)).zipWithConserve(mt.paramTypes)(typedExpr) + var args0 = outers ::: args ++ protoArgs(pt) + if (args0.length > MaxImplementedFunctionArity && mt.paramTypes.length == 1) { + val bunchedArgs = untpd.JavaSeqLiteral(args0, TypeTree(defn.ObjectType)) + .withType(defn.ArrayOf(defn.ObjectType)) + args0 = bunchedArgs :: Nil + } + val args1 = args0.zipWithConserve(mt.paramTypes)(typedExpr) untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType case _ => throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") @@ -470,18 +502,36 @@ object Erasure extends TypeTestsCasts{ super.typedValDef(untpd.cpy.ValDef(vdef)( tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym) + /** Besides normal typing, this function also compacts anonymous functions + * with more than `MaxImplementedFunctionArity` parameters to ise a single + * parameter of type `[]Object`. + */ override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { val restpe = if (sym.isConstructor) defn.UnitType else sym.info.resultType + var vparamss1 = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil + var rhs1 = ddef.rhs match { + case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) + case _ => ddef.rhs + } + if (sym.isAnonymousFunction && vparamss1.head.length > MaxImplementedFunctionArity) { + val bunchedParam = ctx.newSymbol(sym, nme.ALLARGS, Flags.TermParam, JavaArrayType(defn.ObjectType)) + def selector(n: Int) = ref(bunchedParam) + .select(defn.Array_apply) + .appliedTo(Literal(Constant(n))) + val paramDefs = vparamss1.head.zipWithIndex.map { + case (paramDef, idx) => + assignType(untpd.cpy.ValDef(paramDef)(rhs = selector(idx)), paramDef.symbol) + } + vparamss1 = (tpd.ValDef(bunchedParam) :: Nil) :: Nil + rhs1 = untpd.Block(paramDefs, rhs1) + } val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, - vparamss = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil, + vparamss = vparamss1, tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), - rhs = ddef.rhs match { - case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) - case _ => ddef.rhs - }) + rhs = rhs1) super.typedDefDef(ddef1, sym) } |