diff options
Diffstat (limited to 'compiler')
6 files changed, 73 insertions, 51 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 111382b18..2722d91eb 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -353,8 +353,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 @@ -362,17 +360,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) @@ -382,24 +401,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) = { @@ -407,13 +431,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 c7b1538c7..a97589d73 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -10,6 +10,7 @@ import scala.collection.{ mutable, immutable } import PartialFunction._ import collection.mutable import util.common.alwaysZero +import typer.Applications object Definitions { @@ -477,6 +478,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") @@ -844,18 +846,7 @@ class Definitions { TupleType(elems.size).appliedTo(elems) } - def isProductSubType(tp: Type)(implicit ctx: Context) = - (tp derivesFrom ProductType.symbol) && tp.baseClasses.exists(isProductClass) - - def productArity(tp: Type)(implicit ctx: Context) = - if (tp derivesFrom ProductType.symbol) - tp.baseClasses.find(isProductClass) match { - case Some(prod) => prod.typeParams.length - case None => -1 - } - else -1 - - /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN ? */ + /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN? */ def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) val sym = tp.dealias.typeSymbol diff --git a/compiler/src/dotty/tools/dotc/repl/ammonite/Protocol.scala b/compiler/src/dotty/tools/dotc/repl/ammonite/Protocol.scala index 34d31aeca..e066050b6 100644 --- a/compiler/src/dotty/tools/dotc/repl/ammonite/Protocol.scala +++ b/compiler/src/dotty/tools/dotc/repl/ammonite/Protocol.scala @@ -5,7 +5,7 @@ package ammonite.terminal case class TermInfo(ts: TermState, width: Int) -sealed trait TermAction +trait TermAction case class Printing(ts: TermState, stdout: String) extends TermAction case class TermState( inputs: LazyList[Int], diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index d87412d93..41a1218eb 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -233,7 +233,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { // next: MatchMonad[U] // returns MatchMonad[U] def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { - val resultArity = defn.productArity(b.info) + val resultArity = productArity(b.info) if (isProductMatch(prev.tpe, resultArity)) { val nullCheck: Tree = prev.select(defn.Object_ne).appliedTo(Literal(Constant(null))) ifThenElseZero( @@ -1408,7 +1408,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { protected def seqTree(binder: Symbol) = tupleSel(binder)(firstIndexingBinder + 1) protected def tupleSel(binder: Symbol)(i: Int): Tree = { val accessors = - if (defn.isProductSubType(binder.info)) + if (Applications.canProductMatch(binder.info)) productSelectors(binder.info) else binder.caseAccessors val res = diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 2f2af9868..c4d3e2292 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -48,13 +48,15 @@ object Applications { ref.info.widenExpr.dealias } + def canProductMatch(tp: Type)(implicit ctx: Context) = + extractorMemberType(tp, nme._1).exists + /** Does `tp` fit the "product match" conditions as an unapply result type - * for a pattern with `numArgs` subpatterns> - * This is the case of `tp` is a subtype of the Product<numArgs> class. + * for a pattern with `numArgs` subpatterns? + * This is the case of `tp` has members `_1` to `_N` where `N == numArgs`. */ def isProductMatch(tp: Type, numArgs: Int)(implicit ctx: Context) = - 0 <= numArgs && numArgs <= Definitions.MaxTupleArity && - tp.derivesFrom(defn.ProductNType(numArgs).typeSymbol) + numArgs > 0 && productArity(tp) == numArgs /** Does `tp` fit the "get match" conditions as an unapply result type? * This is the case of `tp` has a `get` member as well as a @@ -69,6 +71,9 @@ object Applications { sels.takeWhile(_.exists).toList } + def productArity(tp: Type)(implicit ctx: Context) = + if (canProductMatch(tp)) productSelectorTypes(tp).size else -1 + def productSelectors(tp: Type)(implicit ctx: Context): List[Symbol] = { val sels = for (n <- Iterator.from(0)) yield tp.member(nme.selectorName(n)).symbol sels.takeWhile(_.exists).toList @@ -109,7 +114,7 @@ object Applications { getUnapplySelectors(getTp, args, pos) else if (unapplyResult isRef defn.BooleanClass) Nil - else if (defn.isProductSubType(unapplyResult)) + else if (canProductMatch(unapplyResult)) productSelectorTypes(unapplyResult) // this will cause a "wrong number of arguments in pattern" error later on, // which is better than the message in `fail`. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e6236d122..ba55dfe30 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -762,10 +762,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit /** Is `formal` a product type which is elementwise compatible with `params`? */ def ptIsCorrectProduct(formal: Type) = { - val pclass = defn.ProductNType(params.length).symbol isFullyDefined(formal, ForceDegree.noBottom) && - formal.derivesFrom(pclass) && - formal.baseArgTypes(pclass).corresponds(params) { + Applications.canProductMatch(formal) && + Applications.productSelectorTypes(formal).corresponds(params) { (argType, param) => param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe } |