aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SyntaxSummary.txt15
-rw-r--r--src/dotty/tools/backend/jvm/GenBCode.scala2
-rw-r--r--src/dotty/tools/dotc/config/CompilerCommand.scala8
-rw-r--r--src/dotty/tools/dotc/config/Config.scala27
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala29
-rw-r--r--src/dotty/tools/dotc/core/Decorators.scala2
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala10
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala2
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala17
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala24
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala9
-rw-r--r--src/dotty/tools/dotc/core/Types.scala127
-rw-r--r--src/dotty/tools/dotc/core/Uniques.scala7
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala2
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala28
-rw-r--r--src/dotty/tools/dotc/printing/PlainPrinter.scala5
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala77
-rw-r--r--src/dotty/tools/dotc/printing/Showable.scala7
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala17
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Mode.scala5
-rw-r--r--tests/pos/Iterable.scala52
-rw-r--r--tests/pos/IterableSelfRec.scala52
-rw-r--r--tests/pos/annot.scala13
24 files changed, 423 insertions, 118 deletions
diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt
index e40b3fd6c..1bbdc84d7 100644
--- a/docs/SyntaxSummary.txt
+++ b/docs/SyntaxSummary.txt
@@ -99,8 +99,9 @@ grammar.
FunArgTypes ::= InfixType
| `(' [ FunArgType {`,' FunArgType } ] `)'
InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2)
- RefinedType ::= WithType {Annotation | [nl] Refinement} Annotated(t, annot), RefinedTypeTree(t, ds)
- WithType ::= SimpleType {`with' SimpleType} (deprecated)
+ RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds)
+ WithType ::= AnnotType {`with' AnnotType} (deprecated)
+ AnnotType ::= SimpleType {Annotation} Annotated(t, annot)
SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args)
| SimpleType `#' id SelectFromTypeTree(t, name)
| StableId
@@ -121,9 +122,9 @@ grammar.
Expr ::= FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
| Expr1
- FunParams ::= Bindings
- | [`implicit'] id
- | `_'
+ FunParams ::= Bindings
+ | [`implicit'] id
+ | `_'
ExprInParens ::= PostfixExpr `:' Type
| Expr
BlockResult ::= (FunParams | [`implicit'] id `:' InfixType) => Block
@@ -248,7 +249,7 @@ grammar.
AccessModifier ::= (`private' | `protected') [AccessQualifier]
AccessQualifier ::= `[' (id | `this') `]'
- Annotation ::= `@' SimpleType {ArgumentExprs} Apply(tpe, args)
+ Annotation ::= `@' SimpleType {ParArgumentExprs} Apply(tpe, args)
TemplateBody ::= [nl] `{' [SelfType] TemplateStat {semi TemplateStat} `} (self, stats)
TemplateStat ::= Import
@@ -301,7 +302,7 @@ grammar.
TemplateOpt ::= [`extends' Template | [nl] TemplateBody]
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
ConstrApps ::= ConstrApp {`with' ConstrApp}
- ConstrApp ::= SimpleType {ArgumentExprs} Apply(tp, args)
+ ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
ConstrExpr ::= SelfInvocation
| ConstrBlock
diff --git a/src/dotty/tools/backend/jvm/GenBCode.scala b/src/dotty/tools/backend/jvm/GenBCode.scala
index 7729c3011..db376a452 100644
--- a/src/dotty/tools/backend/jvm/GenBCode.scala
+++ b/src/dotty/tools/backend/jvm/GenBCode.scala
@@ -38,7 +38,7 @@ class GenBCode extends Phase {
def run(implicit ctx: Context): Unit = {
- new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
+ new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
entryPoints.clear()
}
}
diff --git a/src/dotty/tools/dotc/config/CompilerCommand.scala b/src/dotty/tools/dotc/config/CompilerCommand.scala
index 629042291..3ba8db3ba 100644
--- a/src/dotty/tools/dotc/config/CompilerCommand.scala
+++ b/src/dotty/tools/dotc/config/CompilerCommand.scala
@@ -110,19 +110,19 @@ object CompilerCommand extends DotClass {
if (summary.errors.nonEmpty) {
summary.errors foreach (ctx.error(_))
- ctx.echo(" dotc -help gives more information")
+ ctx.println(" dotc -help gives more information")
Nil
}
else if (settings.version.value) {
- ctx.echo(versionMsg)
+ ctx.println(versionMsg)
Nil
}
else if (shouldStopWithInfo) {
- ctx.echo(infoMessage)
+ ctx.println(infoMessage)
Nil
} else {
if (summary.arguments.isEmpty && !settings.resident.value)
- ctx.echo(usageMessage)
+ ctx.println(usageMessage)
summary.arguments
}
}
diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala
index 38ebca078..27d5effa5 100644
--- a/src/dotty/tools/dotc/config/Config.scala
+++ b/src/dotty/tools/dotc/config/Config.scala
@@ -71,4 +71,31 @@ object Config {
/** Check that certain types cannot be created in erasedTypes phases */
final val checkUnerased = true
+
+
+ /** Initial size of superId table */
+ final val InitialSuperIdsSize = 4096
+
+ /** Initial capacity of uniques HashMap */
+ final val initialUniquesCapacity = 40000
+
+ /** How many recursive calls to NamedType#underlying are performed before logging starts. */
+ final val LogPendingUnderlyingThreshold = 50
+
+ /** How many recursive calls to isSubType are performed before logging starts. */
+ final val LogPendingSubTypesThreshold = 50
+
+ /** How many recursive calls to findMember are performed before logging names starts
+ * Note: this threshold has to be chosen carefully. Too large, and programs
+ * like tests/pos/IterableSelfRec go into polynomial (or even exponential?)
+ * compile time slowdown. Too small and normal programs will cause the compiler to
+ * do inefficient operations on findMember. The current value is determined
+ * so that (1) IterableSelfRec still compiles in reasonable time (< 10sec) (2) Compiling
+ * dotty itself only causes small pending names lists to be generated (we measured
+ * at max 6 elements) and these lists are never searched with contains.
+ */
+ final val LogPendingFindMemberThreshold = 10
+
+ /** Maximal number of outstanding recursive calls to findMember */
+ final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4
}
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index 5d2750aa8..c1b2e20eb 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -20,6 +20,7 @@ import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource}
import typer._
import Implicits.ContextualImplicits
import config.Settings._
+import config.Config
import reporting._
import collection.mutable
import collection.immutable.BitSet
@@ -164,6 +165,14 @@ object Contexts {
_typeComparer
}
+ /** Number of findMember calls on stack */
+ private[core] var findMemberCount: Int = 0
+
+ /** List of names which have a findMemberCall on stack,
+ * after Config.LogPendingFindMemberThreshold is reached.
+ */
+ private[core] var pendingMemberSearches: List[Name] = Nil
+
/** The new implicit references that are introduced by this scope */
private var implicitsCache: ContextualImplicits = null
def implicits: ContextualImplicits = {
@@ -508,7 +517,7 @@ object Contexts {
def nextId = { _nextId += 1; _nextId }
/** A map from a superclass id to the typeref of the class that has it */
- private[core] var classOfId = new Array[ClassSymbol](InitialSuperIdsSize)
+ private[core] var classOfId = new Array[ClassSymbol](Config.InitialSuperIdsSize)
/** A map from a the typeref of a class to its superclass id */
private[core] val superIdOfClass = new mutable.AnyRefMap[ClassSymbol, Int]
@@ -529,7 +538,7 @@ object Contexts {
// Types state
/** A table for hash consing unique types */
- private[core] val uniques = new util.HashSet[Type](initialUniquesCapacity) {
+ private[core] val uniques = new util.HashSet[Type](Config.initialUniquesCapacity) {
override def hash(x: Type): Int = x.hash
}
@@ -614,20 +623,4 @@ object Contexts {
myBounds = myBounds.updated(sym, b)
def bounds = myBounds
}
-
- /** Initial size of superId table */
- private final val InitialSuperIdsSize = 4096
-
- /** Initial capacity of uniques HashMap */
- private[core] final val initialUniquesCapacity = 40000
-
- /** How many recursive calls to NamedType#underlying are performed before
- * logging starts.
- */
- private[core] final val LogPendingUnderlyingThreshold = 50
-
- /** How many recursive calls to isSubType are performed before
- * logging starts.
- */
- private[core] final val LogPendingSubTypesThreshold = 50
}
diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala
index 8aaef985c..d90b959ab 100644
--- a/src/dotty/tools/dotc/core/Decorators.scala
+++ b/src/dotty/tools/dotc/core/Decorators.scala
@@ -173,7 +173,7 @@ object Decorators {
def treatSingleArg(arg: Any) : Any =
try
arg match {
- case arg: Showable => arg.show(ctx.fresh.addMode(Mode.FutureDefsOK))
+ case arg: Showable => arg.show(ctx.addMode(Mode.FutureDefsOK))
case _ => arg
}
catch {
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index 60aacd894..bf5e219cf 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -99,12 +99,18 @@ object NameOps {
/** Is this the name of a higher-kinded type parameter of a Lambda? */
def isLambdaArgName =
- name.length > 0 && name.head == tpnme.LAMBDA_ARG_PREFIXhead && name.startsWith(tpnme.LAMBDA_ARG_PREFIX)
+ name.length > 0 &&
+ name.head == tpnme.LAMBDA_ARG_PREFIXhead &&
+ name.startsWith(tpnme.LAMBDA_ARG_PREFIX) && {
+ val digits = name.drop(tpnme.LAMBDA_ARG_PREFIX.length)
+ digits.length <= 4 && digits.forall(_.isDigit)
+ }
/** The index of the higher-kinded type parameter with this name.
* Pre: isLambdaArgName.
*/
- def lambdaArgIndex: Int = name.drop(name.lastIndexOf('$') + 1).toString.toInt
+ def lambdaArgIndex: Int =
+ name.drop(tpnme.LAMBDA_ARG_PREFIX.length).toString.toInt
/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index 14d6bedc7..6273612c7 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -172,7 +172,7 @@ object StdNames {
final val WILDCARD_STAR: N = "_*"
final val REIFY_TREECREATOR_PREFIX: N = "$treecreator"
final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator"
- final val LAMBDA_ARG_PREFIX: N = "$hkArg$"
+ final val LAMBDA_ARG_PREFIX: N = "HK$"
final val LAMBDA_ARG_PREFIXhead: Char = LAMBDA_ARG_PREFIX.head
final val Any: N = "Any"
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index 8bd1651dc..7f3f8a446 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -515,17 +515,19 @@ class TypeApplications(val self: Type) extends AnyVal {
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams)
}
- /** Test whether this type has a base type `B[T1, ..., Tn]` where the type parameters
- * of `B` match one-by-one the variances of `tparams`, and where the lambda
- * abstracted type
+ /** Test whether this type has a base type of the form `B[T1, ..., Bn]` where
+ * the type parameters of `B` match one-by-one the variances of `tparams`,
+ * and where the lambda abstracted type
*
* LambdaXYZ { type Apply = B[$hkArg$0, ..., $hkArg$n] }
* { type $hkArg$0 = T1; ...; type $hkArg$n = Tn }
*
* satisfies predicate `p`. Try base types in the order of their occurrence in `baseClasses`.
* A type parameter matches a variance V if it has V as its variance or if V == 0.
+ * @param classBounds A hint to bound the search. Only types that derive from one of the
+ * classes in classBounds are considered.
*/
- def testLifted(tparams: List[Symbol], p: Type => Boolean)(implicit ctx: Context): Boolean = {
+ def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol])(implicit ctx: Context): Boolean = {
def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match {
case bc :: bcs1 =>
val tp = self.baseTypeWithArgs(bc)
@@ -533,7 +535,8 @@ class TypeApplications(val self: Type) extends AnyVal {
val tycon = tp.withoutArgs(targs)
def variancesMatch(param1: Symbol, param2: Symbol) =
param2.variance == param2.variance || param2.variance == 0
- if ((tycon.typeParams corresponds tparams)(variancesMatch)) {
+ if (classBounds.exists(tycon.derivesFrom(_)) &&
+ tycon.typeParams.corresponds(tparams)(variancesMatch)) {
val expanded = tycon.EtaExpand
val lifted = (expanded /: targs) { (partialInst, targ) =>
val tparam = partialInst.typeParams.head
@@ -548,7 +551,7 @@ class TypeApplications(val self: Type) extends AnyVal {
false
}
if (tparams.isEmpty) false
- else if (typeParams.nonEmpty) p(EtaExpand) || tryLift(self.baseClasses)
- else tryLift(self.baseClasses)
+ else if (typeParams.nonEmpty) p(EtaExpand) || classBounds.nonEmpty && tryLift(self.baseClasses)
+ else classBounds.nonEmpty && tryLift(self.baseClasses)
}
}
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 18f9f08bb..069525db0 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -97,7 +97,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
try {
recCount = recCount + 1
val result =
- if (recCount < LogPendingSubTypesThreshold) firstTry(tp1, tp2)
+ if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2)
else monitoredIsSubType(tp1, tp2)
recCount = recCount - 1
if (!result) constraint = saved
@@ -345,7 +345,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
|| fourthTry(tp1, tp2)
)
normalPath ||
- needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2))
+ needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2), classBounds(tp2))
}
else // fast path, in particular for refinements resulting from parameterization.
isSubType(tp1, skipped2) &&
@@ -453,7 +453,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
case tp1: RefinedType =>
isNewSubType(tp1.parent, tp2) ||
- needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _))
+ needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil)
case AndType(tp11, tp12) =>
eitherIsSubType(tp11, tp2, tp12, tp2)
case JavaArrayType(elem1) =>
@@ -475,9 +475,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
val lambda = projection.prefix.LambdaClass(forcing = true)
lambda.exists && !other.isLambda &&
other.testLifted(lambda.typeParams,
- if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix))
+ if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix),
+ if (inOrder) Nil else classBounds(projection.prefix))
}
+ /** The class symbols bounding the type of the `Apply` member of `tp` */
+ private def classBounds(tp: Type) = tp.member(tpnme.Apply).info.classSymbols
+
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
* to keep the constraint as wide as possible. Specifically, if
*
@@ -1141,13 +1145,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
}
/** Show subtype goal that led to an assertion failure */
- def showGoal(tp1: Type, tp2: Type) = {
- println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
+ def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = {
+ ctx.println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
def explainPoly(tp: Type) = tp match {
- case tp: PolyParam => println(s"polyparam ${tp.show} found in ${tp.binder.show}")
- case tp: TypeRef if tp.symbol.exists => println(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
- case tp: TypeVar => println(s"typevar ${tp.show}, origin = ${tp.origin}")
- case _ => println(s"${tp.show} is a ${tp.getClass}")
+ case tp: PolyParam => ctx.println(s"polyparam ${tp.show} found in ${tp.binder.show}")
+ case tp: TypeRef if tp.symbol.exists => ctx.println(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
+ case tp: TypeVar => ctx.println(s"typevar ${tp.show}, origin = ${tp.origin}")
+ case _ => ctx.println(s"${tp.show} is a ${tp.getClass}")
}
explainPoly(tp1)
explainPoly(tp2)
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index 1adabbd2d..acbd5b6f0 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -302,18 +302,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
// println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG
var refinements: SimpleMap[TypeName, Type] = SimpleMap.Empty
var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty
- def normalizeToRef(tp: Type): TypeRef = tp match {
+ def normalizeToRef(tp: Type): TypeRef = tp.dealias match {
+ case tp: TypeRef =>
+ tp
case tp @ RefinedType(tp1, name: TypeName) =>
val prevInfo = refinements(name)
refinements = refinements.updated(name,
if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo)
formals = formals.updated(name, tp1.typeParamNamed(name))
normalizeToRef(tp1)
- case tp: TypeRef =>
- if (tp.symbol.info.isAlias) normalizeToRef(tp.info.bounds.hi)
- else tp
case ErrorType =>
defn.AnyClass.typeRef
+ case AnnotatedType(_, tpe) =>
+ normalizeToRef(tpe)
case _ =>
throw new TypeError(s"unexpected parent type: $tp")
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 3ab621db1..ae9088f00 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -35,8 +35,6 @@ import language.implicitConversions
object Types {
- private var recCount = 0 // used temporarily for debugging. TODO: remove
-
private var nextId = 0
/** The class of types.
@@ -417,9 +415,7 @@ object Types {
* as seen from given prefix `pre`. Exclude all members that have
* flags in `excluded` from consideration.
*/
- final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = try {
- recCount += 1
- assert(recCount < 40)
+ final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
@tailrec def go(tp: Type): Denotation = tp match {
case tp: RefinedType =>
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
@@ -456,7 +452,11 @@ object Types {
if (tp.refinementRefersToThis) tp.refinedInfo.substSkolem(tp, pre)
else tp.refinedInfo
if (name.isTypeName) { // simplified case that runs more efficiently
- val jointInfo = if (rinfo.isAlias) rinfo else pdenot.info & rinfo
+ val jointInfo =
+ if (rinfo.isAlias) rinfo
+ else if (pdenot.info.isAlias) pdenot.info
+ else if (ctx.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo)
+ else pdenot.info & rinfo
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
} else
pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre)
@@ -488,15 +488,31 @@ object Types {
}
def goAnd(l: Type, r: Type) = go(l) & (go(r), pre)
def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
- go(this)
- } catch {
- case ex: MergeError =>
- throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
- case ex: Throwable =>
- println(s"findMember exception for $this member $name")
- throw ex // DEBUG
- } finally {
- recCount -= 1
+ def safeAnd(tp1: Type, tp2: Type): Type = (tp1, tp2) match {
+ case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
+ case _ => tp1 & tp2
+ }
+
+ { val recCount = ctx.findMemberCount + 1
+ ctx.findMemberCount = recCount
+ if (recCount >= Config.LogPendingFindMemberThreshold)
+ ctx.pendingMemberSearches = name :: ctx.pendingMemberSearches
+ }
+
+ try go(this)
+ catch {
+ case ex: MergeError =>
+ throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
+ case ex: Throwable =>
+ ctx.println(i"findMember exception for $this member $name")
+ throw ex // DEBUG
+ }
+ finally {
+ val recCount = ctx.findMemberCount
+ if (recCount >= Config.LogPendingFindMemberThreshold)
+ ctx.pendingMemberSearches = ctx.pendingMemberSearches.tail
+ ctx.findMemberCount = recCount - 1
+ }
}
/** The set of names of members of this type that pass the given name filter
@@ -729,6 +745,12 @@ object Types {
case tp => tp
}
+ /** If this is a TypeAlias type, its alias otherwise this type itself */
+ final def followTypeAlias(implicit ctx: Context): Type = this match {
+ case TypeAlias(alias) => alias
+ case _ => this
+ }
+
/** Perform successive widenings and dealiasings until none can be applied anymore */
final def widenDealias(implicit ctx: Context): Type = {
val res = this.widen.dealias
@@ -786,43 +808,73 @@ object Types {
// ----- Normalizing typerefs over refined types ----------------------------
- /** If this is a refinement type that has a refinement for `name` (which might be followed
+ /** If this normalizes* to a refinement type that has a refinement for `name` (which might be followed
* by other refinements), and the refined info is a type alias, return the alias,
* otherwise return NoType. Used to reduce types of the form
*
* P { ... type T = / += / -= U ... } # T
*
* to just U. Does not perform the reduction if the resulting type would contain
- * a reference to the "this" of the current refined type. But does follow
- * aliases in order to avoid such references. Example:
+ * a reference to the "this" of the current refined type, except in the following situation
*
- * Lambda$I { type $hk$Arg0 = String, type Apply = Lambda$I{...}.$hk$Arg0 } # Apply
+ * (1) The "this" reference can be avoided by following an alias. Example:
*
- * Here, the refinement for `Apply` has a refined this node, yet dereferencing ones more
- * yields `String` as the result of lookupRefined.
+ * P { type T = String, type R = P{...}.T } # R --> String
+ *
+ * (2) The refinement is a fully instantiated type lambda, and the projected name is "Apply".
+ * In this case the rhs of the apply is returned with all references to lambda argument types
+ * substituted by their definitions.
+ *
+ * (*) normalizes means: follow instantiated typevars and aliases.
*/
def lookupRefined(name: Name)(implicit ctx: Context): Type = {
- def loop(pre: Type): Type = pre.stripTypeVar match {
+ def loop(pre: Type, resolved: List[Name]): Type = pre.stripTypeVar match {
case pre: RefinedType =>
- if (pre.refinedName ne name) loop(pre.parent)
- else pre.refinedInfo match {
- case TypeAlias(tp) =>
- if (!pre.refinementRefersToThis) tp
- else tp match {
- case TypeRef(SkolemType(`pre`), alias) => lookupRefined(alias)
- case _ => NoType
+ object instantiate extends TypeMap {
+ var isSafe = true
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(SkolemType(`pre`), name) if name.isLambdaArgName =>
+ val TypeAlias(alias) = member(name).info
+ alias
+ case tp: TypeVar if !tp.inst.exists =>
+ isSafe = false
+ tp
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ def betaReduce(tp: Type) = {
+ val lam = pre.parent.LambdaClass(forcing = false)
+ if (lam.exists && lam.typeParams.forall(tparam => resolved.contains(tparam.name))) {
+ val reduced = instantiate(tp)
+ if (instantiate.isSafe) reduced else NoType
+ }
+ else NoType
+ }
+ pre.refinedInfo match {
+ case TypeAlias(alias) =>
+ if (pre.refinedName ne name) loop(pre.parent, pre.refinedName :: resolved)
+ else if (!pre.refinementRefersToThis) alias
+ else alias match {
+ case TypeRef(SkolemType(`pre`), aliasName) => lookupRefined(aliasName) // (1)
+ case _ => if (name == tpnme.Apply) betaReduce(alias) else NoType // (2)
}
- case _ => loop(pre.parent)
+ case _ => loop(pre.parent, resolved)
}
case SkolemType(binder) =>
binder.lookupRefined(name)
case pre: WildcardType =>
WildcardType
+ case pre: TypeRef =>
+ pre.info match {
+ case TypeAlias(alias) => loop(alias, resolved)
+ case _ => NoType
+ }
case _ =>
NoType
}
- loop(this)
+ loop(this, Nil)
}
/** The type <this . name> , reduced if possible */
@@ -1390,7 +1442,7 @@ object Types {
def isTerm = isInstanceOf[TermRef]
/** Guard against cycles that can arise if given `op`
- * follows info. The prblematic cases are a type alias to itself or
+ * follows info. The problematic cases are a type alias to itself or
* bounded by itself or a val typed as itself:
*
* type T <: T
@@ -1401,7 +1453,7 @@ object Types {
*/
final def controlled[T](op: => T)(implicit ctx: Context): T = try {
ctx.underlyingRecursions += 1
- if (ctx.underlyingRecursions < LogPendingUnderlyingThreshold)
+ if (ctx.underlyingRecursions < Config.LogPendingUnderlyingThreshold)
op
else if (ctx.pendingUnderlying contains this)
throw CyclicReference(symbol)
@@ -1751,6 +1803,11 @@ object Types {
lazy val ref = refFn()
override def underlying(implicit ctx: Context) = ref
override def toString = s"LazyRef($ref)"
+ override def equals(other: Any) = other match {
+ case other: LazyRef => this.ref.equals(other.ref)
+ case _ => false
+ }
+ override def hashCode = ref.hashCode + 37
}
// --- Refined Type ---------------------------------------------------------
@@ -1842,7 +1899,7 @@ object Types {
else make(RefinedType(parent, names.head, infoFns.head), names.tail, infoFns.tail)
def apply(parent: Type, name: Name, infoFn: RefinedType => Type)(implicit ctx: Context): RefinedType = {
- assert(!ctx.erasedTypes)
+ assert(!ctx.erasedTypes || ctx.mode.is(Mode.Printing))
ctx.base.uniqueRefinedTypes.enterIfNew(new CachedRefinedType(parent, name, infoFn)).checkInst
}
@@ -2590,7 +2647,7 @@ object Types {
abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) {
/** pre: this is a type alias */
def derivedTypeAlias(tp: Type, variance: Int = this.variance)(implicit ctx: Context) =
- if (lo eq tp) this
+ if ((lo eq tp) && (variance == this.variance)) this
else TypeAlias(tp, variance)
override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = {
diff --git a/src/dotty/tools/dotc/core/Uniques.scala b/src/dotty/tools/dotc/core/Uniques.scala
index c24b0cabc..b00508d60 100644
--- a/src/dotty/tools/dotc/core/Uniques.scala
+++ b/src/dotty/tools/dotc/core/Uniques.scala
@@ -2,6 +2,7 @@ package dotty.tools.dotc
package core
import Types._, Contexts._, util.Stats._, Hashable._, Names._
+import config.Config
import util.HashSet
/** Defines operation `unique` for hash-consing types.
@@ -39,7 +40,7 @@ object Uniques {
)
*/
- final class NamedTypeUniques extends HashSet[NamedType](initialUniquesCapacity) with Hashable {
+ final class NamedTypeUniques extends HashSet[NamedType](Config.initialUniquesCapacity) with Hashable {
override def hash(x: NamedType): Int = x.hash
private def findPrevious(h: Int, prefix: Type, name: Name): NamedType = {
@@ -65,7 +66,7 @@ object Uniques {
}
}
- final class TypeAliasUniques extends HashSet[TypeAlias](initialUniquesCapacity) with Hashable {
+ final class TypeAliasUniques extends HashSet[TypeAlias](Config.initialUniquesCapacity) with Hashable {
override def hash(x: TypeAlias): Int = x.hash
private def findPrevious(h: Int, alias: Type, variance: Int): TypeAlias = {
@@ -90,7 +91,7 @@ object Uniques {
}
}
- final class RefinedUniques extends HashSet[RefinedType](initialUniquesCapacity) with Hashable {
+ final class RefinedUniques extends HashSet[RefinedType](Config.initialUniquesCapacity) with Hashable {
override val hashSeed = classOf[CachedRefinedType].hashCode // some types start life as CachedRefinedTypes, need to have same hash seed
override def hash(x: RefinedType): Int = x.hash
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index 84d497b9c..e753bdcab 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -720,7 +720,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
case SELECT =>
def readQual(name: Name) = {
val localCtx =
- if (name == nme.CONSTRUCTOR) ctx.fresh.addMode(Mode.InSuperCall) else ctx
+ if (name == nme.CONSTRUCTOR) ctx.addMode(Mode.InSuperCall) else ctx
readTerm()(localCtx)
}
def readRest(name: Name, sig: Signature) = {
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index dd2c9bcaa..b79077245 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -679,25 +679,29 @@ object Parsers {
def refinedTypeRest(t: Tree): Tree = {
newLineOptWhenFollowedBy(LBRACE)
- in.token match {
- case AT => refinedTypeRest(atPos(t.pos.start) { Annotated(annot(), t) })
- case LBRACE => refinedTypeRest(atPos(t.pos.start) { RefinedTypeTree(t, refinement()) })
- case _ => t
- }
+ if (in.token == LBRACE) refinedTypeRest(atPos(t.pos.start) { RefinedTypeTree(t, refinement()) })
+ else t
}
- /** WithType ::= SimpleType {`with' SimpleType} (deprecated)
+ /** WithType ::= AnnotType {`with' AnnotType} (deprecated)
*/
- def withType(): Tree = withTypeRest(simpleType())
+ def withType(): Tree = withTypeRest(annotType())
- def withTypeRest(t: Tree): Tree = {
+ def withTypeRest(t: Tree): Tree =
if (in.token == WITH) {
deprecationWarning("`with' as a type operator has been deprecated; use `&' instead")
in.nextToken()
AndTypeTree(t, withType())
}
else t
- }
+
+ /** AnnotType ::= SimpleType {Annotation}
+ */
+ def annotType(): Tree = annotTypeRest(simpleType())
+
+ def annotTypeRest(t: Tree): Tree =
+ if (in.token == AT) annotTypeRest(atPos(t.pos.start) { Annotated(annot(), t) })
+ else t
/** SimpleType ::= SimpleType TypeArgs
* | SimpleType `#' Id
@@ -1425,10 +1429,10 @@ object Parsers {
else tree1
}
- /** Annotation ::= `@' SimpleType {ArgumentExprs}
+ /** Annotation ::= `@' SimpleType {ParArgumentExprs}
*/
def annot() =
- adjustStart(accept(AT)) { ensureApplied(argumentExprss(wrapNew(simpleType()))) }
+ adjustStart(accept(AT)) { ensureApplied(parArgumentExprss(wrapNew(simpleType()))) }
def annotations(skipNewLines: Boolean = false): List[Tree] = {
if (skipNewLines) newLineOptWhenFollowedBy(AT)
@@ -1834,7 +1838,7 @@ object Parsers {
/** ConstrApp ::= SimpleType {ParArgumentExprs}
*/
val constrApp = () => {
- val t = simpleType()
+ val t = annotType()
if (in.token == LPAREN) parArgumentExprss(wrapNew(t))
else t
}
diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala
index fb1c0fc74..12c94677f 100644
--- a/src/dotty/tools/dotc/printing/PlainPrinter.scala
+++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala
@@ -8,10 +8,11 @@ import StdNames.nme
import ast.Trees._, ast._
import java.lang.Integer.toOctalString
import config.Config.summarizeDepth
+import typer.Mode
import scala.annotation.switch
class PlainPrinter(_ctx: Context) extends Printer {
- protected[this] implicit def ctx: Context = _ctx
+ protected[this] implicit def ctx: Context = _ctx.fresh.addMode(Mode.Printing)
protected def maxToTextRecursions = 100
@@ -169,6 +170,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
else TypeBounds.empty
"(" ~ toText(tp.origin) ~ "?" ~ toText(bounds) ~ ")"
}
+ case tp: LazyRef =>
+ "LazyRef(" ~ toTextGlobal(tp.ref) ~ ")"
case _ =>
tp.fallbackToText(this)
}
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index 11d451255..cc8f0bef8 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -5,7 +5,7 @@ import core._
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._
import TypeErasure.ErasedValueType
import Contexts.Context, Scopes.Scope, Denotations._, SymDenotations._, Annotations.Annotation
-import StdNames.nme
+import StdNames.{nme, tpnme}
import ast.{Trees, untpd, tpd}
import typer.Namer
import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto, dummyTreeOfType}
@@ -121,6 +121,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close
}
+ if (tp.isSafeLambda) {
+ val (prefix, body, bindings) = extractApply(tp)
+ prefix match {
+ case prefix: TypeRef if prefix.symbol.isLambdaTrait && body.exists =>
+ return typeLambdaText(prefix.symbol, body, bindings)
+ case _ =>
+ }
+ }
case tp: TypeRef =>
val hideType = tp.symbol is AliasPreferred
if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) {
@@ -159,6 +167,73 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
def blockText[T >: Untyped](trees: List[Tree[T]]): Text =
"{" ~ toText(trees, "\n") ~ "}"
+ /** If type `tp` represents a potential type Lambda of the form
+ *
+ * parent { type Apply = body; argBindings? }
+ *
+ * split it into
+
+ * - the `parent`
+ * - the simplified `body`
+ * - the bindings HK$ members, if there are any
+ *
+ * The body is simplified as follows
+ * - if it is a TypeAlias, follow it
+ * - replace all references to of the form <skolem>.HK$i by references
+ * without a prefix, because the latter print nicer.
+ *
+ */
+ def extractApply(tp: Type): (Type, Type, List[(Name, Type)]) = tp.stripTypeVar match {
+ case tp @ RefinedType(parent, name) =>
+ if (name == tpnme.Apply) {
+ // simplify arguments so that parameters just print HK$i and not
+ // LambdaI{...}.HK$i
+ val simplifyArgs = new TypeMap {
+ override def apply(tp: Type) = tp match {
+ case tp @ TypeRef(SkolemType(_), name) if name.isLambdaArgName =>
+ TypeRef(NoPrefix, tp.symbol.asType)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ (parent, simplifyArgs(tp.refinedInfo.followTypeAlias), Nil)
+ } else if (name.isLambdaArgName) {
+ val (prefix, body, argBindings) = extractApply(parent)
+ (prefix, body, (name, tp.refinedInfo) :: argBindings)
+ } else (tp, NoType, Nil)
+ case _ =>
+ (tp, NoType, Nil)
+ }
+
+ /** The text for a TypeLambda
+ *
+ * LambdaXYZ { type Apply = body'; bindings? }
+ *
+ * where
+ * @param lambdaCls The class symbol for `LambdaXYZ`
+ * @param body The simplified lambda body
+ * @param bindings The bindings of any HK$i arguments
+ *
+ * @return A text of the form
+ *
+ * [HK$0, ..., HK$n] => body
+ *
+ * possibly followed by bindings
+ *
+ * [HK$i = arg_i, ..., HK$k = arg_k]
+ */
+ def typeLambdaText(lambdaCls: Symbol, body: Type, bindings: List[(Name, Type)]): Text = {
+ def lambdaParamText(tparam: Symbol): Text = {
+ varianceString(tparam) ~ nameString(tparam.name)
+ }
+ def lambdaText = changePrec(GlobalPrec) {
+ "[" ~ Text(lambdaCls.typeParams.map(lambdaParamText), ", ") ~ "] => " ~ toTextGlobal(body)
+ }
+ def bindingText(binding: (Name, Type)) = binding._1.toString ~ toTextGlobal(binding._2)
+ if (bindings.isEmpty) lambdaText
+ else atPrec(DotPrec)(lambdaText) ~ "[" ~ Text(bindings.map(bindingText), ", ") ~ "]"
+ }
+
override def toText[T >: Untyped](tree: Tree[T]): Text = controlled {
import untpd.{modsDeco => _, _}
diff --git a/src/dotty/tools/dotc/printing/Showable.scala b/src/dotty/tools/dotc/printing/Showable.scala
index 550d80060..37de053cb 100644
--- a/src/dotty/tools/dotc/printing/Showable.scala
+++ b/src/dotty/tools/dotc/printing/Showable.scala
@@ -5,6 +5,7 @@ import core._
import Contexts._, Texts._, Decorators._
import config.Config.summarizeDepth
+import scala.util.control.NonFatal
trait Showable extends Any {
@@ -20,7 +21,11 @@ trait Showable extends Any {
def fallbackToText(printer: Printer): Text = toString
/** The string representation of this showable element. */
- def show(implicit ctx: Context): String = toText(ctx.printer).show
+ def show(implicit ctx: Context): String =
+ try toText(ctx.printer).show
+ catch {
+ case NonFatal(ex) => s"[cannot display due to $ex, raw string = $toString]"
+ }
/** The summarized string representation of this showable element.
* Recursion depth is limited to some smallish value. Default is
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index 71a908397..7c5bab673 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -11,6 +11,7 @@ import config.Settings.Setting
import config.Printers
import java.lang.System.currentTimeMillis
import typer.ErrorReporting.DiagnosticString
+import typer.Mode
object Reporter {
@@ -73,9 +74,9 @@ trait Reporting { this: Context =>
/** For sending messages that are printed only if -verbose is set */
def inform(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- if (this.settings.verbose.value) echo(msg, pos)
+ if (this.settings.verbose.value) this.println(msg, pos)
- def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
+ def println(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(new Info(msg, pos))
def deprecationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
@@ -111,7 +112,7 @@ trait Reporting { this: Context =>
*/
def log(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
if (this.settings.log.value.containsPhase(phase))
- echo(s"[log ${ctx.phasesStack.reverse.mkString(" -> ")}] $msg", pos)
+ this.println(s"[log ${ctx.phasesStack.reverse.mkString(" -> ")}] $msg", pos)
def debuglog(msg: => String): Unit =
if (ctx.debug) log(msg)
@@ -215,7 +216,7 @@ abstract class Reporter {
}
def report(d: Diagnostic)(implicit ctx: Context): Unit = if (!isHidden(d)) {
- doReport(d)
+ doReport(d)(ctx.addMode(Mode.Printing))
d match {
case d: ConditionalWarning if !d.enablingOption.value => unreportedWarnings(d.enablingOption.name) += 1
case d: Warning => warningCount += 1
@@ -231,10 +232,10 @@ abstract class Reporter {
/** Print a summary */
def printSummary(implicit ctx: Context): Unit = {
- if (warningCount > 0) ctx.echo(countString(warningCount, "warning") + " found")
- if (errorCount > 0) ctx.echo(countString(errorCount, "error") + " found")
+ if (warningCount > 0) ctx.println(countString(warningCount, "warning") + " found")
+ if (errorCount > 0) ctx.println(countString(errorCount, "error") + " found")
for ((settingName, count) <- unreportedWarnings)
- ctx.echo(s"there were $count ${settingName.tail} warning(s); re-run with $settingName for details")
+ ctx.println(s"there were $count ${settingName.tail} warning(s); re-run with $settingName for details")
}
/** Returns a string meaning "n elements". */
@@ -248,7 +249,7 @@ abstract class Reporter {
}
/** Should this diagnostic not be reported at all? */
- def isHidden(d: Diagnostic)(implicit ctx: Context): Boolean = false
+ def isHidden(d: Diagnostic)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing)
/** Does this reporter contain not yet reported errors or warnings? */
def hasPending: Boolean = false
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index d665aa0c5..507dbb0ce 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -21,6 +21,7 @@ import ast.Trees._
import Applications._
import TypeApplications._
import SymUtils._, core.NameOps._
+import typer.Mode
import dotty.tools.dotc.util.Positions.Position
import dotty.tools.dotc.core.Decorators._
@@ -464,8 +465,9 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
// all potentially stored subpat binders
val potentiallyStoredBinders = stored.unzip._1.toSet
// compute intersection of all symbols in the tree `in` and all potentially stored subpat binders
- new DeepFolder[Unit]((x: Unit, t:Tree) =>
+ def computeBinders(implicit ctx: Context) = new DeepFolder[Unit]((x: Unit, t:Tree) =>
if (potentiallyStoredBinders(t.symbol)) usedBinders += t.symbol).apply((), in)
+ computeBinders(ctx.addMode(Mode.FutureDefsOK)) // trigged a NotDefinedHere on $outer when compiler dotc/printing
if (usedBinders.isEmpty) in
else {
diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala
index 997741819..8889cf604 100644
--- a/src/dotty/tools/dotc/typer/Mode.scala
+++ b/src/dotty/tools/dotc/typer/Mode.scala
@@ -63,5 +63,10 @@ object Mode {
*/
val AllowDependentFunctions = newMode(9, "AllowDependentFunctions")
+ /** We are currently printing something: avoid to produce more logs about
+ * the printing
+ */
+ val Printing = newMode(10, "Printing")
+
val PatternOrType = Pattern | Type
}
diff --git a/tests/pos/Iterable.scala b/tests/pos/Iterable.scala
new file mode 100644
index 000000000..715667e7a
--- /dev/null
+++ b/tests/pos/Iterable.scala
@@ -0,0 +1,52 @@
+package dotty.collections
+package immutable
+
+import annotation.unchecked.uncheckedVariance
+
+trait Collection[+CC[X] <: Collection[CC, X], T] {
+ def companion: CollectionCompanion[CC]
+}
+
+trait Iterable[T] extends Collection[Iterable, T] {
+ def iterator: Iterator[T]
+ override def companion: IterableCompanion[Iterable] = Iterable
+}
+
+trait Seq[T] extends Iterable[T] with Collection[Seq, T] {
+ def apply(x: Int): T
+ override def companion: IterableCompanion[Seq] = Seq
+}
+
+abstract class CollectionCompanion[+CC[X] <: Collection[CC, X]]
+
+trait IterableImpls[CC[X]] {
+ def fromIterator[T](it: Iterator[T]): CC[T]
+ def toIterator[T](xs: CC[T]): Iterator[T]
+ def map[T, U](xs: CC[T], f: T => U): CC[U] =
+ fromIterator(toIterator(xs).map(f))
+ def filter[T](xs: CC[T], p: T => Boolean): CC[T] =
+ fromIterator(toIterator(xs).filter(p))
+ def flatMap[T, U](xs: CC[T], f: T => TraversableOnce[U]): CC[U] =
+ fromIterator(toIterator(xs).flatMap(f))
+}
+
+abstract class IterableCompanion[+CC[X] <: Iterable[X] with Collection[CC, X]]
+extends CollectionCompanion[CC] with IterableImpls[CC] @uncheckedVariance {
+ def toIterator[T](xs: CC[T] @uncheckedVariance) = xs.iterator
+ implicit def transformOps[T](xs: CC[T] @uncheckedVariance): TransformOps[CC, T] = new TransformOps[CC, T](xs)
+}
+
+class TransformOps[+CC[X] <: Iterable[X] with Collection[CC, X], T] (val xs: CC[T]) extends AnyVal {
+ def companion[T](xs: CC[T] @uncheckedVariance): IterableCompanion[CC] = xs.companion.asInstanceOf
+ def map[U](f: T => U): CC[U] = companion(xs).map(xs, f)
+ def filter(p: T => Boolean): CC[T] = companion(xs).filter(xs, p)
+ def flatMap[U](f: T => TraversableOnce[U]): CC[U] = companion(xs).flatMap(xs, f)
+}
+
+object Iterable extends IterableCompanion[Iterable] {
+ def fromIterator[T](it: Iterator[T]): Iterable[T] = ???
+}
+object Seq extends IterableCompanion[Seq] {
+ def fromIterator[T](it: Iterator[T]): Seq[T] = ???
+}
+
diff --git a/tests/pos/IterableSelfRec.scala b/tests/pos/IterableSelfRec.scala
new file mode 100644
index 000000000..bba7a82d2
--- /dev/null
+++ b/tests/pos/IterableSelfRec.scala
@@ -0,0 +1,52 @@
+package dotty.collection
+package immutable
+
+import annotation.unchecked.uncheckedVariance
+
+trait Collection[T] { self =>
+ type This <: Collection { type This <: self.This }
+ def companion: CollectionCompanion[This]
+}
+
+trait Iterable[T] extends Collection[T] { self =>
+ type This <: Iterable { type This <: self.This }
+ override def companion: IterableCompanion[This] = Iterable.asInstanceOf
+
+ def iterator: Iterator[T]
+}
+
+trait Seq[T] extends Iterable[T] { self =>
+ type This <: Seq { type This <: self.This }
+ override def companion: IterableCompanion[This] = Seq.asInstanceOf
+
+ def apply(x: Int): T
+}
+
+abstract class CollectionCompanion[+CC <: Collection { type This <: CC }]
+
+abstract class IterableCompanion[+CC <: Iterable { type This <: CC }] extends CollectionCompanion[CC] {
+ def fromIterator[T](it: Iterator[T]): CC[T]
+ def map[T, U](xs: Iterable[T], f: T => U): CC[U] =
+ fromIterator(xs.iterator.map(f))
+ def filter[T](xs: Iterable[T], p: T => Boolean): CC[T] =
+ fromIterator(xs.iterator.filter(p))
+ def flatMap[T, U](xs: Iterable[T], f: T => TraversableOnce[U]): CC[U] =
+ fromIterator(xs.iterator.flatMap(f))
+
+ implicit def transformOps[T](xs: CC[T] @uncheckedVariance): TransformOps[CC, T] = ??? // new TransformOps[CC, T](xs)
+}
+
+class TransformOps[+CC <: Iterable { type This <: CC }, T] (val xs: CC[T]) extends AnyVal {
+ def companion[T](xs: CC[T] @uncheckedVariance): IterableCompanion[CC] = xs.companion
+ def map[U](f: T => U): CC[U] = companion(xs).map(xs, f)
+ def filter(p: T => Boolean): CC[T] = companion(xs).filter(xs, p)
+ def flatMap[U](f: T => TraversableOnce[U]): CC[U] = companion(xs).flatMap(xs, f)
+}
+
+object Iterable extends IterableCompanion[Iterable] {
+ def fromIterator[T](it: Iterator[T]): Iterable[T] = ???
+}
+object Seq extends IterableCompanion[Seq] {
+ def fromIterator[T](it: Iterator[T]): Seq[T] = ???
+}
+
diff --git a/tests/pos/annot.scala b/tests/pos/annot.scala
index c3a17ff1d..e6e4f8051 100644
--- a/tests/pos/annot.scala
+++ b/tests/pos/annot.scala
@@ -1,7 +1,10 @@
import java.beans.Transient
+import annotation.unchecked.uncheckedVariance
class Test {
+// testing combinations of annotation syntax
+
@SuppressWarnings(Array("hi")) def foo() = ??? // evalutation of annotation on type cannot be deferred as requires implicit resolution(only generic Array$.apply applies here)
@SuppressWarnings(Array("hi", "foo")) def foo2() = ??? //can be deferred as there is a non-generic method
@@ -11,5 +14,15 @@ class Test {
@Transient(false) def bar = ???
@Transient() def baz = ???
+
+// testing annotations in types
+
+ class A
+ trait B
+
+ val x: A @uncheckedVariance with B @uncheckedVariance = ???
+
+ class C extends A @uncheckedVariance () with B @uncheckedVariance { val x = 10 }
+
}