From c0ff8adcb4f44a0b01077495a7626d201f8100d5 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 6 Apr 2017 18:52:58 +0200 Subject: Generate synthetic productElement/productArity methods above 22 --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 83 ++++++++++++++-------- .../src/dotty/tools/dotc/core/Definitions.scala | 1 + 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 66fc6bf84..45b339727 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -352,8 +352,6 @@ object desugar { lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef) // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams) - // def isDefined = true - // def productArity = N // def _1 = this.p1 // ... // def _N = this.pN @@ -361,17 +359,38 @@ object desugar { // pN: TN = pN: @uncheckedVariance)(moreParams) = // new C[...](p1, ..., pN)(moreParams) // + // Above arity 22 we also synthesize: + // def productArity = N + // def productElement(i: Int): Any = i match { ... } + // // Note: copy default parameters need @uncheckedVariance; see // neg/t1843-variances.scala for a test case. The test would give // two errors without @uncheckedVariance, one of them spurious. - val caseClassMeths = - if (isCaseClass) { - def syntheticProperty(name: TermName, rhs: Tree) = - DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic) + val caseClassMeths = { + def syntheticProperty(name: TermName, rhs: Tree) = + DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic) + def productArity = syntheticProperty(nme.productArity, Literal(Constant(arity))) + def productElement = { + val param = makeSyntheticParameter(tpt = ref(defn.IntType)) + // case N => _${N + 1} + val cases = 0.until(arity).map { i => + CaseDef(Literal(Constant(i)), EmptyTree, Select(This(EmptyTypeIdent), nme.selectorName(i))) + } + val ioob = ref(defn.IndexOutOfBoundsException.typeRef) + val error = Throw(New(ioob, List(List(Select(refOfDef(param), nme.toString_))))) + // case _ => throw new IndexOutOfBoundsException(i.toString) + val defaultCase = CaseDef(untpd.Ident(nme.WILDCARD), EmptyTree, error) + val body = Match(refOfDef(param), (cases :+ defaultCase).toList) + DefDef(nme.productElement, Nil, List(List(param)), TypeTree(defn.AnyType), body) + .withMods(synthetic) + } + def productElemMeths = { val caseParams = constrVparamss.head.toArray - val productElemMeths = - for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name) - yield syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) + for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name) + yield syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) + } + def enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil + def copyMeths = { def isRepeated(tree: Tree): Boolean = tree match { case PostfixOp(_, Ident(nme.raw.STAR)) => true case ByNameTypeTree(tree1) => isRepeated(tree1) @@ -381,24 +400,29 @@ object desugar { case ValDef(_, tpt, _) => isRepeated(tpt) case _ => false }) - - val copyMeths = - if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued - else { - def copyDefault(vparam: ValDef) = - makeAnnotated("scala.annotation.unchecked.uncheckedVariance", refOfDef(vparam)) - val copyFirstParams = derivedVparamss.head.map(vparam => - cpy.ValDef(vparam)(rhs = copyDefault(vparam))) - val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => - cpy.ValDef(vparam)(rhs = EmptyTree)) - DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) - .withMods(synthetic) :: Nil - } - - val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil - copyMeths ::: enumTagMeths ::: productElemMeths.toList + if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued + else { + def copyDefault(vparam: ValDef) = + makeAnnotated("scala.annotation.unchecked.uncheckedVariance", refOfDef(vparam)) + val copyFirstParams = derivedVparamss.head.map(vparam => + cpy.ValDef(vparam)(rhs = copyDefault(vparam))) + val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => + cpy.ValDef(vparam)(rhs = EmptyTree)) + DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) + .withMods(synthetic) :: Nil + } } + + // Above MaxTupleArity we extend Product instead of ProductN, in this + // case we need to synthesise productElement & productArity. + def largeProductMeths = + if (arity > Definitions.MaxTupleArity) productElement :: productArity :: Nil + else Nil + + if (isCaseClass) + largeProductMeths ::: copyMeths ::: enumTagMeths ::: productElemMeths.toList else Nil + } def anyRef = ref(defn.AnyRefAlias.typeRef) def productConstr(n: Int) = { @@ -406,13 +430,16 @@ object desugar { val targs = constrVparamss.head map (_.tpt) if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs) } + def product = + if (arity > Definitions.MaxTupleArity) scalaDot(nme.Product.toTypeName) + else productConstr(arity) - // Case classes and case objects get a ProductN parent + // Case classes and case objects get Product/ProductN parents var parents1 = parents if (isEnumCase && parents.isEmpty) parents1 = enumClassTypeRef :: Nil - if (mods.is(Case) && arity <= Definitions.MaxTupleArity) - parents1 = parents1 :+ productConstr(arity) // TODO: This also adds Product0 to caes objects. Do we want that? + if (mods.is(Case)) + parents1 = parents1 :+ product // TODO: This also adds Product0 to case objects. Do we want that? if (isEnum) parents1 = parents1 :+ ref(defn.EnumType) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b70fcb093..337f48064 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -477,6 +477,7 @@ class Definitions { lazy val JavaCloneableClass = ctx.requiredClass("java.lang.Cloneable") lazy val NullPointerExceptionClass = ctx.requiredClass("java.lang.NullPointerException") + lazy val IndexOutOfBoundsException = ctx.requiredClass("java.lang.IndexOutOfBoundsException") lazy val ClassClass = ctx.requiredClass("java.lang.Class") lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number") lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable") -- cgit v1.2.3