From 93a2d0653e6b74a0f88825ac8a522da87e474f2a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 9 Feb 2017 14:45:42 +0100 Subject: 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. --- .../src/dotty/tools/dotc/core/Definitions.scala | 90 +++++++++++++++------- compiler/src/dotty/tools/dotc/core/NameOps.scala | 45 +++++++++-- .../src/dotty/tools/dotc/core/TypeErasure.scala | 11 ++- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 3 +- .../src/dotty/tools/dotc/transform/Erasure.scala | 6 +- .../dotty/tools/dotc/transform/TreeChecker.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 +- 7 files changed, 111 insertions(+), 52 deletions(-) (limited to 'compiler/src/dotty/tools/dotc') diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2797bb8a6..69ab6c21a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -635,13 +635,9 @@ class Definitions { FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil) def unapply(ft: Type)(implicit ctx: Context) = { val tsym = ft.typeSymbol - val isImplicitFun = isImplicitFunctionClass(tsym) - if (isImplicitFun || isFunctionClass(tsym)) { - val targs = ft.argInfos - val numArgs = targs.length - 1 - if (numArgs >= 0 && FunctionType(numArgs, isImplicitFun).symbol == tsym) - Some(targs.init, targs.last, isImplicitFun) - else None + if (isFunctionClass(tsym)) { + val targs = ft.dealias.argInfos + Some(targs.init, targs.last, tsym.name.isImplicitFunction) } else None } @@ -694,20 +690,17 @@ class Definitions { lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2) lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0) - def FunctionClass(n: Int)(implicit ctx: Context) = - if (n < MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n) + def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context) = + if (isImplicit) ctx.requiredClass("scala.ImplicitFunction" + n.toString) + else 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 ImplicitFunctionClass(n: Int)(implicit ctx: Context) = - ctx.requiredClass("scala.ImplicitFunction" + n.toString) - def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef = - if (isImplicit && !ctx.erasedTypes) ImplicitFunctionClass(n).typeRef - else if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n) - else FunctionClass(n).typeRef + if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n) + else FunctionClass(n, isImplicit).typeRef private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet @@ -731,14 +724,61 @@ class Definitions { def isBottomType(tp: Type) = tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) - def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) - def isImplicitFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.ImplicitFunction) - /** Is a class that will be erased to FunctionXXL */ - def isXXLFunctionClass(cls: Symbol) = cls.name.functionArity > MaxImplementedFunctionArity + /** Is a function class. + * - FunctionN for N >= 0 + * - ImplicitFunctionN for N >= 0 + */ + def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction + + /** Is an implicit function class. + * - ImplicitFunctionN for N >= 0 + */ + def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction + + /** Is a class that will be erased to FunctionXXL + * - FunctionN for N >= 22 + * - ImplicitFunctionN for N >= 22 + */ + def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity + + /** Is a synthetic function class + * - FunctionN for N > 22 + * - ImplicitFunctionN for N >= 0 + */ + def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction + def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) + /** Returns the erased class of the function class `cls` + * - FunctionN for N > 22 becomes FunctionXXL + * - FunctionN for 22 > N >= 0 remains as FunctionN + * - ImplicitFunctionN for N > 22 becomes FunctionXXL + * - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN + * - anything else becomes a NoSymbol + */ + def erasedFunctionClass(cls: Symbol): Symbol = { + val arity = scalaClassName(cls).functionArity + if (arity > 22) defn.FunctionXXLClass + else if (arity >= 0) defn.FunctionClass(arity) + else NoSymbol + } + + /** Returns the erased type of the function class `cls` + * - FunctionN for N > 22 becomes FunctionXXL + * - FunctionN for 22 > N >= 0 remains as FunctionN + * - ImplicitFunctionN for N > 22 becomes FunctionXXL + * - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN + * - anything else becomes a NoType + */ + def erasedFunctionType(cls: Symbol): Type = { + val arity = scalaClassName(cls).functionArity + if (arity > 22) defn.FunctionXXLType + else if (arity >= 0) defn.FunctionType(arity) + else NoType + } + val predefClassNames: Set[Name] = Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName) @@ -809,16 +849,13 @@ class Definitions { def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) val sym = tp.dealias.typeSymbol - arity >= 0 && ( - isFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = false).typeSymbol) || - isImplicitFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = true).typeSymbol) - ) + arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol) } def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 def isImplicitFunctionType(tp: Type)(implicit ctx: Context) = - isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction) + isFunctionType(tp) && tp.dealias.typeSymbol.name.isImplicitFunction // ----- primitive value class machinery ------------------------------------------ @@ -892,9 +929,6 @@ class Definitions { // ----- Initialization --------------------------------------------------- - private def maxImplemented(name: Name) = - if (name `startsWith` tpnme.Function) MaxImplementedFunctionArity else 0 - /** Give the scala package a scope where a FunctionN trait is automatically * added when someone looks for it. */ @@ -904,7 +938,7 @@ class Definitions { val newDecls = new MutableScope(oldDecls) { override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { val res = super.lookupEntry(name) - if (res == null && name.isTypeName && name.functionArity > maxImplemented(name)) + if (res == null && name.isTypeName && name.isSyntheticFunction) newScopeEntry(newFunctionNTrait(name.asTypeName)) else res } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index c037d1ce7..cd3ae2a25 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -8,6 +8,7 @@ import Names._, StdNames._, Contexts._, Symbols._, Flags._ import Decorators.StringDecorator import util.{Chars, NameTransformer} import Chars.isOperatorPart +import Definitions._ object NameOps { @@ -231,13 +232,43 @@ object NameOps { } } - def functionArity: Int = { - def test(prefix: Name): Int = - if (name.startsWith(prefix)) - try name.drop(prefix.length).toString.toInt - catch { case ex: NumberFormatException => -1 } - else -1 - test(tpnme.Function) max test(tpnme.ImplicitFunction) + /** Is a synthetic function name + * - N for FunctionN + * - N for ImplicitFunctionN + * - (-1) otherwise + */ + def functionArity: Int = + functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction) + + /** Is a function name + * - FunctionN for N >= 0 + * - ImplicitFunctionN for N >= 0 + * - false otherwise + */ + def isFunction: Boolean = functionArity >= 0 + + /** Is a implicit function name + * - ImplicitFunctionN for N >= 0 + * - false otherwise + */ + def isImplicitFunction: Boolean = functionArityFor(tpnme.ImplicitFunction) >= 0 + + /** Is a synthetic function name + * - FunctionN for N > 22 + * - ImplicitFunctionN for N >= 0 + * - false otherwise + */ + def isSyntheticFunction: Boolean = { + functionArityFor(tpnme.Function) > MaxImplementedFunctionArity || + functionArityFor(tpnme.ImplicitFunction) >= 0 + } + + /** Parsed function arity for function with some specific prefix */ + private def functionArityFor(prefix: Name): Int = { + if (name.startsWith(prefix)) + try name.toString.substring(prefix.length).toInt + catch { case _: NumberFormatException => -1 } + else -1 } /** The name of the generic runtime operation corresponding to an array operation */ diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 2a341390b..ff99008bb 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -44,7 +44,7 @@ object TypeErasure { val sym = tp.symbol sym.isClass && sym != defn.AnyClass && sym != defn.ArrayClass && - !defn.isXXLFunctionClass(sym) && !defn.isImplicitFunctionClass(sym) + !defn.isSyntheticFunctionClass(sym) case _: TermRef => true case JavaArrayType(elem) => @@ -358,8 +358,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.isXXLFunctionClass(sym)) defn.FunctionXXLType - else if (defn.isImplicitFunctionClass(sym)) apply(defn.FunctionType(sym.name.functionArity)) + else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -370,7 +369,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case SuperType(thistpe, supertpe) => SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => - defn.FunctionClass(0).typeRef + defn.FunctionType(0) case AndType(tp1, tp2) => erasedGlb(this(tp1), this(tp2), isJava) case OrType(tp1, tp2) => @@ -496,8 +495,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean val erasedVCRef = eraseDerivedValueClassRef(tp) if (erasedVCRef.exists) return sigName(erasedVCRef) } - if (defn.isImplicitFunctionClass(sym)) - sigName(defn.FunctionType(sym.name.functionArity)) + if (defn.isSyntheticFunctionClass(sym)) + sigName(defn.erasedFunctionType(sym)) else normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayOf(elem) => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5b75df6b2..3d952f425 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -126,8 +126,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case AppliedType(tycon, args) => val cls = tycon.typeSymbol if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" - if (defn.isFunctionClass(cls)) return toTextFunction(args, isImplicit = false) - if (defn.isImplicitFunctionClass(cls)) return toTextFunction(args, isImplicit = true) + if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction) if (defn.isTupleClass(cls)) return toTextTuple(args) return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close case tp: TypeRef => diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index bba97c4c2..34ea2bc6f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -349,10 +349,8 @@ object Erasure extends TypeTestsCasts{ if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) { assert(sym.isConstructor, s"${sym.showLocated}") defn.ObjectClass - } else if (defn.isXXLFunctionClass(owner)) - defn.FunctionXXLClass - else if (defn.isImplicitFunctionClass(owner)) - recur(defn.FunctionClass(owner.name.functionArity)) + } else if (defn.isSyntheticFunctionClass(owner)) + defn.erasedFunctionClass(owner) else owner recur(sym.owner) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index eee429a87..b2b99160b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -81,7 +81,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.syntheticCoreClasses.contains(sym) || (sym eq defn.ObjectClass) || (sym is NoSuperClass) || (sym.asClass.superClass.exists) if (!validSuperclass) printError(s"$sym has no superclass set") diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ded8993fb..e04667223 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -664,9 +664,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) { - val funCls = - if (tree.isInstanceOf[untpd.ImplicitFunction]) defn.ImplicitFunctionClass(args.length) - else defn.FunctionClass(args.length) + val funCls = defn.FunctionClass(args.length, tree.isInstanceOf[untpd.ImplicitFunction]) typed(cpy.AppliedTypeTree(tree)( untpd.TypeTree(funCls.typeRef), args :+ body), pt) } @@ -1937,7 +1935,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit !untpd.isImplicitClosure(tree) && !isApplyProto(pt) && !ctx.isAfterTyper) { - typr.println("insert apply on implicit $tree") + typr.println(i"insert apply on implicit $tree") typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) } else if (ctx.mode is Mode.Pattern) { -- cgit v1.2.3