From 1f9d1da086bca3d1dc544e795e37000740844909 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 15 Feb 2018 15:45:39 +0100 Subject: [PATCH 1/5] Rewrite deeply nested for loop with while loops The inner closure is hot and couldn't be inlined according the jvm debug info. --- .../core/unpickleScala2/PickleBuffer.scala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala index 2a789dca99a1..92baaab565e6 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala @@ -252,13 +252,22 @@ object PickleBuffer { // Convert map so that it maps chunks of ChunkBits size at once // instead of single bits. def chunkMap(xs: Array[Long]): FlagMap = { - val chunked = Array.ofDim[Long]( - (xs.length + ChunkBits - 1) / ChunkBits, ChunkSize) - for (i <- 0 until chunked.length) - for (j <- 0 until ChunkSize) - for (k <- 0 until ChunkBits) + val size = (xs.length + ChunkBits - 1) / ChunkBits + val chunked = Array.ofDim[Long](size, ChunkSize) + var i = 0 + while (i < size) { + var j = 0 + while (j < ChunkSize) { + var k = 0 + while (k < ChunkBits) { if ((j & (1 << k)) != 0) chunked(i)(j) |= xs(i * ChunkBits + k) + k += 1 + } + j += 1 + } + i += 1 + } chunked } From b89af44ec81c75de12b424858e479962d9730ab2 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 15 Feb 2018 15:45:39 +0100 Subject: [PATCH 2/5] Remove AndOrType --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 6 +- .../tools/dotc/core/CheckRealizable.scala | 3 +- .../tools/dotc/core/ConstraintHandling.scala | 15 ++- .../tools/dotc/core/OrderingConstraint.scala | 39 ++++-- .../tools/dotc/core/SymDenotations.scala | 7 +- .../tools/dotc/core/TypeApplications.scala | 6 +- .../dotty/tools/dotc/core/TypeComparer.scala | 12 +- .../dotty/tools/dotc/core/TypeErasure.scala | 3 +- .../src/dotty/tools/dotc/core/Types.scala | 124 +++++++++--------- .../tools/dotc/core/tasty/TreePickler.scala | 7 +- .../src/dotty/tools/dotc/sbt/ExtractAPI.scala | 14 +- .../dotty/tools/dotc/typer/Variances.scala | 6 +- 12 files changed, 136 insertions(+), 106 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 6b25ed7da9b1..a358c433dd4d 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -679,7 +679,9 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => rname == tree.name || hasRefinement(parent) case tp: TypeProxy => hasRefinement(tp.underlying) - case tp: AndOrType => + case tp: AndType => + hasRefinement(tp.tp1) || hasRefinement(tp.tp2) + case tp: OrType => hasRefinement(tp.tp1) || hasRefinement(tp.tp2) case _ => false @@ -731,4 +733,4 @@ object TreeInfo { val Pure = new PurityLevel(2) val Idempotent = new PurityLevel(1) val Impure = new PurityLevel(0) -} \ No newline at end of file +} diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index f0c8469ad035..05925421bdb0 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -88,7 +88,8 @@ class CheckRealizable(implicit ctx: Context) { def isConcrete(tp: Type): Boolean = tp.dealias match { case tp: TypeRef => tp.symbol.isClass case tp: TypeProxy => isConcrete(tp.underlying) - case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2) + case tp: AndType => isConcrete(tp.tp1) && isConcrete(tp.tp2) + case tp: OrType => isConcrete(tp.tp1) && isConcrete(tp.tp2) case _ => false } if (!isConcrete(tp)) NotConcrete diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 2fda43a675a9..11cf1449564a 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -53,7 +53,8 @@ trait ConstraintHandling { val b = bound.dealias (b eq param) || { b match { - case b: AndOrType => occursIn(b.tp1) || occursIn(b.tp2) + case b: AndType => occursIn(b.tp1) || occursIn(b.tp2) + case b: OrType => occursIn(b.tp1) || occursIn(b.tp2) case b: TypeVar => occursIn(b.origin) case b: TermRef => occursIn(b.underlying) case _ => false @@ -256,7 +257,8 @@ trait ConstraintHandling { def isFullyDefined(tp: Type): Boolean = tp match { case tp: TypeVar => tp.isInstantiated && isFullyDefined(tp.instanceOpt) case tp: TypeProxy => isFullyDefined(tp.underlying) - case tp: AndOrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2) + case tp: AndType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2) + case tp: OrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2) case _ => true } def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match { @@ -430,10 +432,15 @@ trait ConstraintHandling { * @return The pruned type if all `addLess` calls succeed, `NoType` otherwise. */ def prune(bound: Type): Type = bound match { - case bound: AndOrType => + case bound: AndType => val p1 = prune(bound.tp1) val p2 = prune(bound.tp2) - if (p1.exists && p2.exists) bound.derivedAndOrType(p1, p2) + if (p1.exists && p2.exists) bound.derivedAndType(p1, p2) + else NoType + case bound: OrType => + val p1 = prune(bound.tp1) + val p2 = prune(bound.tp2) + if (p1.exists && p2.exists) bound.derivedOrType(p1, p2) else NoType case bound: TypeVar if constraint contains bound.origin => prune(bound.underlying) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index af2eb3c04fe6..73ed67a08208 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -222,10 +222,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def dependentParams(tp: Type, isUpper: Boolean): List[TypeParamRef] = tp match { case param: TypeParamRef if contains(param) => param :: (if (isUpper) upper(param) else lower(param)) - case tp: AndOrType => - val ps1 = dependentParams(tp.tp1, isUpper) - val ps2 = dependentParams(tp.tp2, isUpper) - if (isUpper == tp.isAnd) ps1.union(ps2) else ps1.intersect(ps2) + case tp: AndType => dependentParams(tp.tp1, isUpper).union (dependentParams(tp.tp2, isUpper)) + case tp: OrType => dependentParams(tp.tp1, isUpper).intersect(dependentParams(tp.tp2, isUpper)) case _ => Nil } @@ -260,11 +258,18 @@ class OrderingConstraint(private val boundsMap: ParamBounds, case param: TypeParamRef if contains(param) => if (!paramBuf.contains(param)) paramBuf += param NoType - case tp: AndOrType if isUpper == tp.isAnd => + case tp: AndType if isUpper => val tp1 = stripParams(tp.tp1, paramBuf, isUpper) val tp2 = stripParams(tp.tp2, paramBuf, isUpper) if (tp1.exists) - if (tp2.exists) tp.derivedAndOrType(tp1, tp2) + if (tp2.exists) tp.derivedAndType(tp1, tp2) + else tp1 + else tp2 + case tp: OrType if !isUpper => + val tp1 = stripParams(tp.tp1, paramBuf, isUpper) + val tp2 = stripParams(tp.tp2, paramBuf, isUpper) + if (tp1.exists) + if (tp2.exists) tp.derivedOrType(tp1, tp2) else tp1 else tp2 case _ => @@ -395,24 +400,32 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def replaceParam(tp: Type, atPoly: TypeLambda, atIdx: Int): Type = tp match { case bounds @ TypeBounds(lo, hi) => - def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { - val tp1 = op(andor.tp1, isUpper) - val tp2 = op(andor.tp2, isUpper) - if ((tp1 eq andor.tp1) && (tp2 eq andor.tp2)) andor - else if (andor.isAnd) tp1 & tp2 + def recombineAnd(and: AndType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { + val tp1 = op(and.tp1, isUpper) + val tp2 = op(and.tp2, isUpper) + if (tp1.eq(and.tp1) && tp2.eq(and.tp2)) and + else tp1 & tp2 + } + + def recombineOr(or: OrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { + val tp1 = op(or.tp1, isUpper) + val tp2 = op(or.tp2, isUpper) + if (tp1.eq(or.tp1) && tp2.eq(or.tp2)) or else tp1 | tp2 } def normalize(tp: Type, isUpper: Boolean): Type = tp match { case p: TypeParamRef if p.binder == atPoly && p.paramNum == atIdx => if (isUpper) defn.AnyType else defn.NothingType - case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, normalize, isUpper) + case tp: AndType if isUpper => recombineAnd(tp, normalize, isUpper) + case tp: OrType if !isUpper => recombineOr (tp, normalize, isUpper) case _ => tp } def replaceIn(tp: Type, isUpper: Boolean): Type = tp match { case `param` => normalize(replacement, isUpper) - case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, replaceIn, isUpper) + case tp: AndType if isUpper => recombineAnd(tp, replaceIn, isUpper) + case tp: OrType if !isUpper => recombineOr (tp, replaceIn, isUpper) case _ => tp.substParam(param, replacement) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b1d300c922e9..4946e535cc64 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1202,7 +1202,8 @@ object SymDenotations { case tp: ExprType => hasSkolems(tp.resType) case tp: AppliedType => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) - case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) + case tp: AndType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) + case tp: OrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) case tp: AnnotatedType => hasSkolems(tp.tpe) case _ => false } @@ -1641,9 +1642,9 @@ object SymDenotations { case tp: TypeRef if tp.symbol.isClass => true case tp: TypeVar => tp.inst.exists && inCache(tp.inst) //case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing - //case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) case tp: TypeProxy => isCachable(tp.underlying, btrCache) - case tp: AndOrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) + case tp: AndType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) + case tp: OrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) case _ => true } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 47155e79af52..0eefd44b109e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -374,8 +374,10 @@ class TypeApplications(val self: Type) extends AnyVal { tryReduce case dealiased: PolyType => dealiased.instantiate(args) - case dealiased: AndOrType => - dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args)) + case dealiased: AndType => + dealiased.derivedAndType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args)) + case dealiased: OrType => + dealiased.derivedOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args)) case dealiased: TypeAlias => dealiased.derivedTypeAlias(dealiased.alias.appliedTo(args)) case dealiased: TypeBounds => diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 3e2fc62c7a7f..4090adf032b4 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -925,7 +925,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp @ RefinedType(parent, rname, rinfo) => tp.derivedRefinedType(fix(parent), rname, rinfo) case tp: TypeParamRef => fixOrElse(bounds(tp).hi, tp) case tp: TypeProxy => fixOrElse(tp.underlying, tp) - case tp: AndOrType => tp.derivedAndOrType(fix(tp.tp1), fix(tp.tp2)) + case tp: AndType => tp.derivedAndType(fix(tp.tp1), fix(tp.tp2)) + case tp: OrType => tp.derivedOrType (fix(tp.tp1), fix(tp.tp2)) case tp => tp } def fixOrElse(tp: Type, fallback: Type) = { @@ -1075,7 +1076,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp: AppliedType => isCovered(tp.tycon) case tp: RefinedOrRecType => isCovered(tp.parent) case tp: AnnotatedType => isCovered(tp.underlying) - case tp: AndOrType => isCovered(tp.tp1) && isCovered(tp.tp2) + case tp: AndType => isCovered(tp.tp1) && isCovered(tp.tp2) + case tp: OrType => isCovered(tp.tp1) && isCovered(tp.tp2) case _ => false } @@ -1323,10 +1325,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { Nil } - private def recombineAndOr(tp: AndOrType, tp1: Type, tp2: Type) = + private def recombineAnd(tp: AndType, tp1: Type, tp2: Type) = if (!tp1.exists) tp2 else if (!tp2.exists) tp1 - else tp.derivedAndOrType(tp1, tp2) + else tp.derivedAndType(tp1, tp2) /** If some (&-operand of) this type is a supertype of `sub` replace it with `NoType`. */ @@ -1334,7 +1336,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { if (isSubTypeWhenFrozen(sub, tp)) NoType else tp match { case tp @ AndType(tp1, tp2) => - recombineAndOr(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub)) + recombineAnd(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub)) case _ => tp } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 75a2db3186f7..6077ceb4325a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -322,7 +322,8 @@ object TypeErasure { case tp: TypeParamRef => false case tp: TypeBounds => false case tp: TypeProxy => hasStableErasure(tp.superType) - case tp: AndOrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) + case tp: AndType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) + case tp: OrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) case _ => false } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 5260c920c737..43e5b1a74401 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2403,36 +2403,21 @@ object Types { // --- AndType/OrType --------------------------------------------------------------- - abstract class AndOrType extends CachedGroundType with ValueType { - def tp1: Type - def tp2: Type - def isAnd: Boolean - def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type // needed? - + abstract case class AndType(tp1: Type, tp2: Type) extends CachedGroundType with ValueType { private[this] var myBaseClassesPeriod: Period = Nowhere private[this] var myBaseClasses: List[ClassSymbol] = _ - - /** Base classes of And are the merge of the operand base classes - * For OrTypes, it's the intersection. - */ + /** Base classes of are the merge of the operand base classes. */ override final def baseClasses(implicit ctx: Context) = { if (myBaseClassesPeriod != ctx.period) { val bcs1 = tp1.baseClasses val bcs1set = BaseClassSet(bcs1) def recur(bcs2: List[ClassSymbol]): List[ClassSymbol] = bcs2 match { case bc2 :: bcs2rest => - if (isAnd) - if (bcs1set contains bc2) - if (bc2.is(Trait)) recur(bcs2rest) - else bcs1 // common class, therefore rest is the same in both sequences - else bc2 :: recur(bcs2rest) - else - if (bcs1set contains bc2) - if (bc2.is(Trait)) bc2 :: recur(bcs2rest) - else bcs2 - else recur(bcs2rest) - case nil => - if (isAnd) bcs1 else bcs2 + if (bcs1set contains bc2) + if (bc2.is(Trait)) recur(bcs2rest) + else bcs1 // common class, therefore rest is the same in both sequences + else bc2 :: recur(bcs2rest) + case nil => bcs1 } myBaseClasses = recur(tp2.baseClasses) myBaseClassesPeriod = ctx.period @@ -2440,25 +2425,6 @@ object Types { myBaseClasses } - override def computeHash(bs: Binders) = doHash(bs, tp1, tp2) - override def stableHash = tp1.stableHash && tp2.stableHash - - override def eql(that: Type) = that match { - case that: AndOrType => isAnd == that.isAnd && tp1.eq(that.tp1) && tp2.eq(that.tp2) - case _ => false - } - - override def iso(that: Any, bs: BinderPairs) = that match { - case that: AndOrType => isAnd == that.isAnd && tp1.equals(that.tp1, bs) && tp2.equals(that.tp2, bs) - case _ => false - } - // equals comes from case classes; no matching override is needed - } - - abstract case class AndType(tp1: Type, tp2: Type) extends AndOrType { - - def isAnd = true - def derivedAndType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this else AndType.make(tp1, tp2, checkValid = true) @@ -2467,8 +2433,12 @@ object Types { if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this else tp1 & tp2 - def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - derivedAndType(tp1, tp2) + override def computeHash(bs: Binders) = doHash(bs, tp1, tp2) + + override def eql(that: Type) = that match { + case that: AndType => tp1.eq(that.tp1) && tp2.eq(that.tp2) + case _ => false + } } final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2) @@ -2497,11 +2467,31 @@ object Types { if (checkValid) apply(tp1, tp2) else unchecked(tp1, tp2) } - abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType { + abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with ValueType { + private[this] var myBaseClassesPeriod: Period = Nowhere + private[this] var myBaseClasses: List[ClassSymbol] = _ + /** Base classes of are the intersection of the operand base classes. */ + override final def baseClasses(implicit ctx: Context) = { + if (myBaseClassesPeriod != ctx.period) { + val bcs1 = tp1.baseClasses + val bcs1set = BaseClassSet(bcs1) + def recur(bcs2: List[ClassSymbol]): List[ClassSymbol] = bcs2 match { + case bc2 :: bcs2rest => + if (bcs1set contains bc2) + if (bc2.is(Trait)) bc2 :: recur(bcs2rest) + else bcs2 + else recur(bcs2rest) + case nil => + bcs2 + } + myBaseClasses = recur(tp2.baseClasses) + myBaseClassesPeriod = ctx.period + } + myBaseClasses + } assert(tp1.isInstanceOf[ValueTypeOrWildcard] && tp2.isInstanceOf[ValueTypeOrWildcard], s"$tp1 $tp2") - def isAnd = false private[this] var myJoin: Type = _ private[this] var myJoinPeriod: Period = Nowhere @@ -2521,8 +2511,12 @@ object Types { if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this else OrType.make(tp1, tp2) - def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - derivedOrType(tp1, tp2) + override def computeHash(bs: Binders) = doHash(bs, tp1, tp2) + + override def eql(that: Type) = that match { + case that: OrType => tp1.eq(that.tp1) && tp2.eq(that.tp2) + case _ => false + } } final class CachedOrType(tp1: Type, tp2: Type) extends OrType(tp1, tp2) @@ -3271,10 +3265,8 @@ object Types { */ def occursIn(bound: Type, fromBelow: Boolean)(implicit ctx: Context): Boolean = bound.stripTypeVar match { case bound: ParamRef => bound == this - case bound: AndOrType => - def occ1 = occursIn(bound.tp1, fromBelow) - def occ2 = occursIn(bound.tp2, fromBelow) - if (fromBelow == bound.isAnd) occ1 && occ2 else occ1 || occ2 + case bound: AndType => occursIn(bound.tp1, fromBelow) && occursIn(bound.tp2, fromBelow) + case bound: OrType => occursIn(bound.tp1, fromBelow) || occursIn(bound.tp2, fromBelow) case _ => false } } @@ -3866,8 +3858,10 @@ object Types { tp.derivedSuperType(thistp, supertp) protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type = tp.derivedAppliedType(tycon, args) - protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type = - tp.derivedAndOrType(tp1, tp2) + protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type): Type = + tp.derivedAndType(tp1, tp2) + protected def derivedOrType(tp: OrType, tp1: Type, tp2: Type): Type = + tp.derivedOrType(tp1, tp2) protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation): Type = tp.derivedAnnotatedType(underlying, annot) protected def derivedWildcardType(tp: WildcardType, bounds: Type): Type = @@ -3959,8 +3953,11 @@ object Types { case tp: ClassInfo => mapClassInfo(tp) - case tp: AndOrType => - derivedAndOrType(tp, this(tp.tp1), this(tp.tp2)) + case tp: AndType => + derivedAndType(tp, this(tp.tp1), this(tp.tp2)) + + case tp: OrType => + derivedOrType(tp, this(tp.tp1), this(tp.tp2)) case tp: SkolemType => tp @@ -4212,11 +4209,13 @@ object Types { else tp.derivedAppliedType(tycon, args) } - override protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type) = - if (isRange(tp1) || isRange(tp2)) - if (tp.isAnd) range(lower(tp1) & lower(tp2), upper(tp1) & upper(tp2)) - else range(lower(tp1) | lower(tp2), upper(tp1) | upper(tp2)) - else tp.derivedAndOrType(tp1, tp2) + override protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type) = + if (isRange(tp1) || isRange(tp2)) range(lower(tp1) & lower(tp2), upper(tp1) & upper(tp2)) + else tp.derivedAndType(tp1, tp2) + + override protected def derivedOrType(tp: OrType, tp1: Type, tp2: Type) = + if (isRange(tp1) || isRange(tp2)) range(lower(tp1) | lower(tp2), upper(tp1) | upper(tp2)) + else tp.derivedOrType(tp1, tp2) override protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation) = underlying match { @@ -4327,7 +4326,10 @@ object Types { this(y, hi) } - case tp: AndOrType => + case tp: AndType => + this(this(x, tp.tp1), tp.tp2) + + case tp: OrType => this(this(x, tp.tp1), tp.tp2) case AnnotatedType(underlying, annot) => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 347278f4fe3d..f9652f19a4d8 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -237,8 +237,11 @@ class TreePickler(pickler: TastyPickler) { case tpe: AnnotatedType => writeByte(ANNOTATEDtype) withLength { pickleType(tpe.tpe, richTypes); pickleTree(tpe.annot.tree) } - case tpe: AndOrType => - writeByte(if (tpe.isAnd) ANDtype else ORtype) + case tpe: AndType => + writeByte(ANDtype) + withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) } + case tpe: OrType => + writeByte(ORtype) withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) } case tpe: ExprType => writeByte(BYNAMEtype) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index d769d23ec535..312e2d4093db 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -450,17 +450,11 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // `recType` would lead to an infinite recursion, we avoid this by // computing the representation of `recType` lazily. apiLazy(recType) - case tp: AndOrType => - val parents = List(apiType(tp.tp1), apiType(tp.tp2)) - - // TODO: Add a real representation for AndOrTypes in xsbti. The order of - // types in an `AndOrType` does not change the API, so the API hash should - // be symmetric. + case tp: AndType => + combineApiTypes(apiType(tp.tp1), apiType(tp.tp2)) + case tp: OrType => val s = combineApiTypes(apiType(tp.tp1), apiType(tp.tp2)) - if (tp.isAnd) - s - else - withMarker(s, orMarker) + withMarker(s, orMarker) case ExprType(resultType) => withMarker(apiType(resultType), byNameMarker) case ConstantType(constant) => diff --git a/compiler/src/dotty/tools/dotc/typer/Variances.scala b/compiler/src/dotty/tools/dotc/typer/Variances.scala index 2635f9c26434..e0e6b79b44a7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Variances.scala +++ b/compiler/src/dotty/tools/dotc/typer/Variances.scala @@ -96,8 +96,10 @@ object Variances { varianceInArgs(varianceInType(tycon)(tparam), args, tycon.typeParams) case AnnotatedType(tp, annot) => varianceInType(tp)(tparam) & varianceInAnnot(annot)(tparam) - case tp: AndOrType => - varianceInType(tp.tp1)(tparam) & varianceInType(tp.tp2)(tparam) + case AndType(tp1, tp2) => + varianceInType(tp1)(tparam) & varianceInType(tp2)(tparam) + case OrType(tp1, tp2) => + varianceInType(tp1)(tparam) & varianceInType(tp2)(tparam) case _ => Bivariant } From 393d4e909386d3a201733f572ebf8105b6d21b7c Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 15 Feb 2018 15:45:39 +0100 Subject: [PATCH 3/5] Specialize Constant constructor --- .../src/dotty/tools/dotc/core/Constants.scala | 64 +++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Constants.scala b/compiler/src/dotty/tools/dotc/core/Constants.scala index a40ef09a894d..4f013a088132 100644 --- a/compiler/src/dotty/tools/dotc/core/Constants.scala +++ b/compiler/src/dotty/tools/dotc/core/Constants.scala @@ -23,28 +23,10 @@ object Constants { final val EnumTag = 13 final val ScalaSymbolTag = 14 - case class Constant(value: Any) extends printing.Showable { + class Constant(val value: Any, val tag: Int) extends printing.Showable with Product1[Any] { import java.lang.Double.doubleToRawLongBits import java.lang.Float.floatToRawIntBits - val tag: Int = value match { - case null => NullTag - case x: Unit => UnitTag - case x: Boolean => BooleanTag - case x: Byte => ByteTag - case x: Short => ShortTag - case x: Int => IntTag - case x: Long => LongTag - case x: Float => FloatTag - case x: Double => DoubleTag - case x: String => StringTag - case x: Char => CharTag - case x: Type => ClazzTag - case x: Symbol => EnumTag - case x: scala.Symbol => ScalaSymbolTag - case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass) - } - def isByteRange: Boolean = isIntRange && Byte.MinValue <= intValue && intValue <= Byte.MaxValue def isShortRange: Boolean = isIntRange && Short.MinValue <= intValue && intValue <= Short.MaxValue def isCharRange: Boolean = isIntRange && Char.MinValue <= intValue && intValue <= Char.MaxValue @@ -235,5 +217,49 @@ object Constants { h = mix(h, equalHashValue.##) finalizeHash(h, length = 2) } + + override def toString = s"Constant($value)" + def canEqual(x: Any) = true + def get = value + def isEmpty = false + def _1 = value + } + + object Constant { + def apply(x: Null) = new Constant(x, NullTag) + def apply(x: Unit) = new Constant(x, UnitTag) + def apply(x: Boolean) = new Constant(x, BooleanTag) + def apply(x: Byte) = new Constant(x, ByteTag) + def apply(x: Short) = new Constant(x, ShortTag) + def apply(x: Int) = new Constant(x, IntTag) + def apply(x: Long) = new Constant(x, LongTag) + def apply(x: Float) = new Constant(x, FloatTag) + def apply(x: Double) = new Constant(x, DoubleTag) + def apply(x: String) = new Constant(x, StringTag) + def apply(x: Char) = new Constant(x, CharTag) + def apply(x: Type) = new Constant(x, ClazzTag) + def apply(x: Symbol) = new Constant(x, EnumTag) + def apply(x: scala.Symbol) = new Constant(x, ScalaSymbolTag) + def apply(value: Any) = + new Constant(value, + value match { + case null => NullTag + case x: Unit => UnitTag + case x: Boolean => BooleanTag + case x: Byte => ByteTag + case x: Short => ShortTag + case x: Int => IntTag + case x: Long => LongTag + case x: Float => FloatTag + case x: Double => DoubleTag + case x: String => StringTag + case x: Char => CharTag + case x: Type => ClazzTag + case x: Symbol => EnumTag + case x: scala.Symbol => ScalaSymbolTag + } + ) + + def unapply(c: Constant) = c } } From 32389f81993bdb5b667a46eae2fbdf0bcb707616 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 15 Feb 2018 15:45:39 +0100 Subject: [PATCH 4/5] Inline xLiteralCommon (the closure was not inlined) --- .../tools/dotc/parsing/MarkupParsers.scala | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala index a8638270e73e..1d74f5aeffc8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala @@ -314,22 +314,6 @@ object MarkupParsers { done } - /** Some try/catch/finally logic used by xLiteral and xLiteralPattern. */ - private def xLiteralCommon(f: () => Tree, ifTruncated: String => Unit): Tree = { - try return f() - catch { - case c @ TruncatedXMLControl => - ifTruncated(c.getMessage) - case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => - parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) - case _: ArrayIndexOutOfBoundsException => - parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) - } - finally parser.in resume Tokens.XMLSTART - - parser.errorTermTree - } - /** Use a lookahead parser to run speculative body, and return the first char afterward. */ private def charComingAfter(body: => Unit): Char = { try { @@ -343,8 +327,8 @@ object MarkupParsers { /** xLiteral = element { element } * @return Scala representation of this xml literal */ - def xLiteral: Tree = xLiteralCommon( - () => { + def xLiteral: Tree = { + try return { input = parser.in handle.isPattern = false @@ -367,15 +351,24 @@ object MarkupParsers { assert(ts.length == 1) ts(0) } - }, - msg => parser.incompleteInputError(msg) - ) + } catch { + case c @ TruncatedXMLControl => + parser.incompleteInputError(c.getMessage) + case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => + parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) + case _: ArrayIndexOutOfBoundsException => + parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) + } + finally parser.in resume Tokens.XMLSTART + + parser.errorTermTree + } /** @see xmlPattern. resynchronizes after successful parse * @return this xml pattern */ - def xLiteralPattern: Tree = xLiteralCommon( - () => { + def xLiteralPattern: Tree = { + try return { input = parser.in saving[Boolean, Tree](handle.isPattern, handle.isPattern = _) { handle.isPattern = true @@ -383,9 +376,18 @@ object MarkupParsers { xSpaceOpt() tree } - }, - msg => parser.syntaxError(msg, curOffset) - ) + } catch { + case c @ TruncatedXMLControl => + parser.syntaxError(c.getMessage, curOffset) + case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => + parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) + case _: ArrayIndexOutOfBoundsException => + parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) + } + finally parser.in resume Tokens.XMLSTART + + parser.errorTermTree + } def escapeToScala[A](op: => A, kind: String) = { xEmbeddedBlock = false From 5c8126944cf043d9c54d36ecae0a0c444bc9d23a Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 15 Feb 2018 15:45:39 +0100 Subject: [PATCH 5/5] Inline xLiteralCommon (the closure was not inlined) --- .../tools/dotc/parsing/MarkupParsers.scala | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala index 1d74f5aeffc8..600d7d45164a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/MarkupParsers.scala @@ -314,6 +314,26 @@ object MarkupParsers { done } + /** Some try/catch/finally logic used by xLiteral and xLiteralPattern. */ + @inline private def xLiteralCommon(f: () => Tree, ifTruncated: String => Unit): Tree = { + var output: Tree = null.asInstanceOf[Tree] + try output = f() + catch { + case c @ TruncatedXMLControl => + ifTruncated(c.getMessage) + case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => + parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) + case _: ArrayIndexOutOfBoundsException => + parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) + } + finally parser.in resume Tokens.XMLSTART + + if (output == null) + parser.errorTermTree + else + output + } + /** Use a lookahead parser to run speculative body, and return the first char afterward. */ private def charComingAfter(body: => Unit): Char = { try { @@ -327,8 +347,8 @@ object MarkupParsers { /** xLiteral = element { element } * @return Scala representation of this xml literal */ - def xLiteral: Tree = { - try return { + def xLiteral: Tree = xLiteralCommon( + () => { input = parser.in handle.isPattern = false @@ -351,24 +371,15 @@ object MarkupParsers { assert(ts.length == 1) ts(0) } - } catch { - case c @ TruncatedXMLControl => - parser.incompleteInputError(c.getMessage) - case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => - parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) - case _: ArrayIndexOutOfBoundsException => - parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) - } - finally parser.in resume Tokens.XMLSTART - - parser.errorTermTree - } + }, + msg => parser.incompleteInputError(msg) + ) /** @see xmlPattern. resynchronizes after successful parse * @return this xml pattern */ - def xLiteralPattern: Tree = { - try return { + def xLiteralPattern: Tree = xLiteralCommon( + () => { input = parser.in saving[Boolean, Tree](handle.isPattern, handle.isPattern = _) { handle.isPattern = true @@ -376,18 +387,9 @@ object MarkupParsers { xSpaceOpt() tree } - } catch { - case c @ TruncatedXMLControl => - parser.syntaxError(c.getMessage, curOffset) - case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => - parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) - case _: ArrayIndexOutOfBoundsException => - parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) - } - finally parser.in resume Tokens.XMLSTART - - parser.errorTermTree - } + }, + msg => parser.syntaxError(msg, curOffset) + ) def escapeToScala[A](op: => A, kind: String) = { xEmbeddedBlock = false