aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dotty/language.scala9
-rw-r--r--src/dotty/tools/backend/jvm/CollectEntryPoints.scala2
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala7
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala28
-rw-r--r--src/dotty/tools/dotc/ast/untpd.scala2
-rw-r--r--src/dotty/tools/dotc/config/Config.scala8
-rw-r--r--src/dotty/tools/dotc/config/PathResolver.scala7
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala8
-rw-r--r--src/dotty/tools/dotc/core/Constraint.scala10
-rw-r--r--src/dotty/tools/dotc/core/ConstraintHandling.scala25
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala2
-rw-r--r--src/dotty/tools/dotc/core/Decorators.scala4
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala10
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala284
-rw-r--r--src/dotty/tools/dotc/core/OrderingConstraint.scala51
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala7
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala20
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala178
-rw-r--r--src/dotty/tools/dotc/core/TypeErasure.scala20
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala137
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala2
-rw-r--r--src/dotty/tools/dotc/core/Types.scala301
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreePickler.scala5
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala4
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala52
-rw-r--r--src/dotty/tools/dotc/printing/Formatting.scala199
-rw-r--r--src/dotty/tools/dotc/printing/Highlighting.scala77
-rw-r--r--src/dotty/tools/dotc/printing/PlainPrinter.scala32
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala5
-rw-r--r--src/dotty/tools/dotc/printing/SyntaxHighlighting.scala88
-rw-r--r--src/dotty/tools/dotc/repl/AmmoniteReader.scala6
-rw-r--r--src/dotty/tools/dotc/repl/CompilingInterpreter.scala30
-rw-r--r--src/dotty/tools/dotc/repl/InterpreterLoop.scala23
-rw-r--r--src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala5
-rw-r--r--src/dotty/tools/dotc/reporting/ConsoleReporter.scala151
-rw-r--r--src/dotty/tools/dotc/reporting/Diagnostic.scala47
-rw-r--r--src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala7
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala85
-rw-r--r--src/dotty/tools/dotc/reporting/StoreReporter.scala15
-rw-r--r--src/dotty/tools/dotc/reporting/ThrowingReporter.scala8
-rw-r--r--src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala23
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/Message.scala106
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala74
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/messages.scala321
-rw-r--r--src/dotty/tools/dotc/sbt/ExtractAPI.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitSelf.scala9
-rw-r--r--src/dotty/tools/dotc/transform/FullParameterization.scala4
-rw-r--r--src/dotty/tools/dotc/transform/LambdaLift.scala5
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala22
-rw-r--r--src/dotty/tools/dotc/transform/Splitter.scala51
-rw-r--r--src/dotty/tools/dotc/transform/TailRec.scala18
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala36
-rw-r--r--src/dotty/tools/dotc/transform/TreeTransform.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala15
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala20
-rw-r--r--src/dotty/tools/dotc/typer/ErrorReporting.scala15
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala8
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala18
-rw-r--r--src/dotty/tools/dotc/typer/RefChecks.scala4
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala10
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala60
-rw-r--r--src/dotty/tools/dotc/typer/Variances.scala2
-rw-r--r--src/dotty/tools/dotc/util/DiffUtil.scala33
-rw-r--r--src/dotty/tools/dotc/util/SourceFile.scala2
-rw-r--r--src/dotty/tools/dotc/util/SourcePosition.scala17
-rw-r--r--src/strawman/collections/CollectionStrawMan4.scala4
68 files changed, 1836 insertions, 1011 deletions
diff --git a/src/dotty/language.scala b/src/dotty/language.scala
deleted file mode 100644
index 416a4281b..000000000
--- a/src/dotty/language.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package dotty
-
-object language {
-
- class Feature
-
- /** Keep union types */
- val keepUnions = new Feature
-}
diff --git a/src/dotty/tools/backend/jvm/CollectEntryPoints.scala b/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
index 3ed232bc7..2ee1b6011 100644
--- a/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
+++ b/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
@@ -107,7 +107,7 @@ object CollectEntryPoints{
else (possibles exists(x=> isJavaMainMethod(x.symbol))) || {
possibles exists { m =>
toDenot(m.symbol).info match {
- case t:PolyType =>
+ case t: PolyType =>
fail("main methods cannot be generic.")
case t@MethodType(paramNames, paramTypes) =>
if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType))
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index ecb6a3212..af34164dc 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -9,6 +9,7 @@ import Decorators._
import language.higherKinds
import collection.mutable.ListBuffer
import util.Property
+import reporting.diagnostic.messages._
object desugar {
import untpd._
@@ -71,7 +72,9 @@ object desugar {
val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next
var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol
if (local.exists) (defctx.owner.thisType select local).dealias
- else throw new Error(s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}")
+ else throw new java.lang.Error(
+ s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}"
+ )
case _ =>
mapOver(tp)
}
@@ -281,7 +284,7 @@ object desugar {
val constrVparamss =
if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty
if (isCaseClass)
- ctx.error("case class needs to have at least one parameter list", cdef.pos)
+ ctx.error(CaseClassMissingParamList(cdef), cdef.namePos)
ListOfNil
}
else constr1.vparamss.nestedMap(toDefParam)
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index 70701ecd7..2c02e7d1e 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -3,8 +3,8 @@ package dotc
package ast
import core._
-import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._, SymDenotations._, Symbols._
-import Denotations._, StdNames._, Comments._
+import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._
+import SymDenotations._, Symbols._, Denotations._, StdNames._, Comments._
import annotation.tailrec
import language.higherKinds
import collection.IndexedSeqOptimized
@@ -48,7 +48,7 @@ object Trees {
* the existing tree transparently, assigning its `tpe` field,
* provided it was `null` before.
* - It is impossible to embed untyped trees in typed ones.
- * - Typed trees can be embedded untyped ones provided they are rooted
+ * - Typed trees can be embedded in untyped ones provided they are rooted
* in a TypedSplice node.
* - Type checking an untyped tree should remove all embedded `TypedSplice`
* nodes.
@@ -308,8 +308,6 @@ object Trees {
if (rawMods.is(Synthetic)) Position(pos.point, pos.point)
else Position(pos.point, pos.point + name.length, pos.point)
else pos
-
-
}
/** A ValDef or DefDef tree */
@@ -564,9 +562,9 @@ object Trees {
}
/** [typeparams] -> tpt */
- case class TypeLambdaTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T])
+ case class PolyTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T])
extends TypTree[T] {
- type ThisTree[-T >: Untyped] = TypeLambdaTree[T]
+ type ThisTree[-T >: Untyped] = PolyTypeTree[T]
}
/** => T */
@@ -822,7 +820,7 @@ object Trees {
type OrTypeTree = Trees.OrTypeTree[T]
type RefinedTypeTree = Trees.RefinedTypeTree[T]
type AppliedTypeTree = Trees.AppliedTypeTree[T]
- type TypeLambdaTree = Trees.TypeLambdaTree[T]
+ type PolyTypeTree = Trees.PolyTypeTree[T]
type ByNameTypeTree = Trees.ByNameTypeTree[T]
type TypeBoundsTree = Trees.TypeBoundsTree[T]
type Bind = Trees.Bind[T]
@@ -855,7 +853,7 @@ object Trees {
val cpy: TreeCopier
- /** A class for copying trees. The copy methods avid creating a new tree
+ /** A class for copying trees. The copy methods avoid creating a new tree
* If all arguments stay the same.
*
* Note: Some of the copy methods take a context.
@@ -986,9 +984,9 @@ object Trees {
case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree
case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args))
}
- def TypeLambdaTree(tree: Tree)(tparams: List[TypeDef], body: Tree): TypeLambdaTree = tree match {
- case tree: TypeLambdaTree if (tparams eq tree.tparams) && (body eq tree.body) => tree
- case _ => finalize(tree, untpd.TypeLambdaTree(tparams, body))
+ def PolyTypeTree(tree: Tree)(tparams: List[TypeDef], body: Tree): PolyTypeTree = tree match {
+ case tree: PolyTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree
+ case _ => finalize(tree, untpd.PolyTypeTree(tparams, body))
}
def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match {
case tree: ByNameTypeTree if result eq tree.result => tree
@@ -1120,8 +1118,8 @@ object Trees {
cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements))
case AppliedTypeTree(tpt, args) =>
cpy.AppliedTypeTree(tree)(transform(tpt), transform(args))
- case TypeLambdaTree(tparams, body) =>
- cpy.TypeLambdaTree(tree)(transformSub(tparams), transform(body))
+ case PolyTypeTree(tparams, body) =>
+ cpy.PolyTypeTree(tree)(transformSub(tparams), transform(body))
case ByNameTypeTree(result) =>
cpy.ByNameTypeTree(tree)(transform(result))
case TypeBoundsTree(lo, hi) =>
@@ -1224,7 +1222,7 @@ object Trees {
this(this(x, tpt), refinements)
case AppliedTypeTree(tpt, args) =>
this(this(x, tpt), args)
- case TypeLambdaTree(tparams, body) =>
+ case PolyTypeTree(tparams, body) =>
implicit val ctx: Context = localCtx
this(this(x, tparams), body)
case ByNameTypeTree(result) =>
diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala
index cc7cefbac..852c3a346 100644
--- a/src/dotty/tools/dotc/ast/untpd.scala
+++ b/src/dotty/tools/dotc/ast/untpd.scala
@@ -212,7 +212,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right)
def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements)
def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args)
- def TypeLambdaTree(tparams: List[TypeDef], body: Tree): TypeLambdaTree = new TypeLambdaTree(tparams, body)
+ def PolyTypeTree(tparams: List[TypeDef], body: Tree): PolyTypeTree = new PolyTypeTree(tparams, body)
def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result)
def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi)
def Bind(name: Name, body: Tree): Bind = new Bind(name, body)
diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala
index c188bfab4..7744a5479 100644
--- a/src/dotty/tools/dotc/config/Config.scala
+++ b/src/dotty/tools/dotc/config/Config.scala
@@ -87,8 +87,12 @@ object Config {
*/
final val checkLambdaVariance = false
- /** Check that certain types cannot be created in erasedTypes phases */
- final val checkUnerased = true
+ /** Check that certain types cannot be created in erasedTypes phases.
+ * Note: Turning this option on will get some false negatives, since it is
+ * possible that And/Or types are still created during erasure as the result
+ * of some operation on an existing type.
+ */
+ final val checkUnerased = false
/** In `derivedSelect`, rewrite
*
diff --git a/src/dotty/tools/dotc/config/PathResolver.scala b/src/dotty/tools/dotc/config/PathResolver.scala
index f9f698e72..55d585e94 100644
--- a/src/dotty/tools/dotc/config/PathResolver.scala
+++ b/src/dotty/tools/dotc/config/PathResolver.scala
@@ -180,6 +180,7 @@ class PathResolver(implicit ctx: Context) {
case "extdirs" => settings.extdirs.value
case "classpath" | "cp" => settings.classpath.value
case "sourcepath" => settings.sourcepath.value
+ case "priorityclasspath" => settings.priorityclasspath.value
}
/** Calculated values based on any given command line options, falling back on
@@ -193,6 +194,7 @@ class PathResolver(implicit ctx: Context) {
def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else ""
def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath)
def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs)
+ def priorityClassPath = cmdLineOrElse("prioritypath", "")
/** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as:
* [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect
* [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg)
@@ -220,7 +222,9 @@ class PathResolver(implicit ctx: Context) {
import context._
// Assemble the elements!
+ // priority class path takes precedence
def basis = List[Traversable[ClassPath]](
+ classesInExpandedPath(priorityClassPath), // 0. The priority class path (for testing).
classesInPath(javaBootClassPath), // 1. The Java bootstrap class path.
contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path.
classesInExpandedPath(javaUserClassPath), // 3. The Java application class path.
@@ -235,6 +239,7 @@ class PathResolver(implicit ctx: Context) {
override def toString = """
|object Calculated {
| scalaHome = %s
+ | priorityClassPath = %s
| javaBootClassPath = %s
| javaExtDirs = %s
| javaUserClassPath = %s
@@ -244,7 +249,7 @@ class PathResolver(implicit ctx: Context) {
| userClassPath = %s
| sourcePath = %s
|}""".trim.stripMargin.format(
- scalaHome,
+ scalaHome, ppcp(priorityClassPath),
ppcp(javaBootClassPath), ppcp(javaExtDirs), ppcp(javaUserClassPath),
useJavaClassPath,
ppcp(scalaBootClassPath), ppcp(scalaExtDirs), ppcp(userClassPath),
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index ff17a9939..8f47e08bf 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -15,6 +15,10 @@ class ScalaSettings extends Settings.SettingGroup {
val javabootclasspath = PathSetting("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath)
val javaextdirs = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs)
val sourcepath = PathSetting("-sourcepath", "Specify location(s) of source files.", "") // Defaults.scalaSourcePath
+ val argfiles = BooleanSetting("@<file>", "A text file containing compiler arguments (options and source files)")
+ val classpath = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp"
+ val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
+ val priorityclasspath = PathSetting("-priorityclasspath", "class path that takes precedence over all other paths (or testing only)", "")
/** Other settings.
*/
@@ -23,6 +27,7 @@ class ScalaSettings extends Settings.SettingGroup {
val migration = BooleanSetting("-migration", "Emit warning and location for migration issues from Scala 2.")
val encoding = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding)
val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail.")
+ val explain = BooleanSetting("-explain", "Explain errors in more detail.")
val feature = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.")
val g = ChoiceSetting("-g", "level", "Set level of generated debugging info.", List("none", "source", "line", "vars", "notailcalls"), "vars")
val help = BooleanSetting("-help", "Print a synopsis of standard options")
@@ -45,9 +50,6 @@ class ScalaSettings extends Settings.SettingGroup {
val nobootcp = BooleanSetting("-nobootcp", "Do not use the boot classpath for the scala jars.")
val strict = BooleanSetting("-strict", "Use strict type rules, which means some formerly legal code does not typecheck anymore.")
- val argfiles = BooleanSetting("@<file>", "A text file containing compiler arguments (options and source files)")
- val classpath = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp"
- val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.")
val language = MultiStringSetting("-language", "feature", "Enable one or more language features.")
val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax")
diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala
index 91e70b7b5..c99b748b7 100644
--- a/src/dotty/tools/dotc/core/Constraint.scala
+++ b/src/dotty/tools/dotc/core/Constraint.scala
@@ -23,7 +23,7 @@ abstract class Constraint extends Showable {
type This <: Constraint
/** Does the constraint's domain contain the type parameters of `pt`? */
- def contains(pt: GenericType): Boolean
+ def contains(pt: PolyType): Boolean
/** Does the constraint's domain contain the type parameter `param`? */
def contains(param: PolyParam): Boolean
@@ -79,7 +79,7 @@ abstract class Constraint extends Showable {
* satisfiability but will solved to give instances of
* type variables.
*/
- def add(poly: GenericType, tvars: List[TypeVar])(implicit ctx: Context): This
+ def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This
/** A new constraint which is derived from this constraint by updating
* the entry for parameter `param` to `tp`.
@@ -121,13 +121,13 @@ abstract class Constraint extends Showable {
* all type parameters of the entry are associated with type variables
* which have their `inst` fields set.
*/
- def isRemovable(pt: GenericType): Boolean
+ def isRemovable(pt: PolyType): Boolean
/** A new constraint with all entries coming from `pt` removed. */
- def remove(pt: GenericType)(implicit ctx: Context): This
+ def remove(pt: PolyType)(implicit ctx: Context): This
/** The polytypes constrained by this constraint */
- def domainPolys: List[GenericType]
+ def domainPolys: List[PolyType]
/** The polytype parameters constrained by this constraint */
def domainParams: List[PolyParam]
diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala
index 5911af72c..3835d553c 100644
--- a/src/dotty/tools/dotc/core/ConstraintHandling.scala
+++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala
@@ -35,10 +35,19 @@ trait ConstraintHandling {
/** If the constraint is frozen we cannot add new bounds to the constraint. */
protected var frozenConstraint = false
- /** We are currently comparing lambdas. Used as a flag for
+ protected var alwaysFluid = false
+
+ /** Perform `op` in a mode where all attempts to set `frozen` to true are ignored */
+ def fluidly[T](op: => T): T = {
+ val saved = alwaysFluid
+ alwaysFluid = true
+ try op finally alwaysFluid = saved
+ }
+
+ /** We are currently comparing polytypes. Used as a flag for
* optimization: when `false`, no need to do an expensive `pruneLambdaParams`
*/
- protected var comparingLambdas = false
+ protected var comparedPolyTypes: Set[PolyType] = Set.empty
private def addOneBound(param: PolyParam, bound: Type, isUpper: Boolean): Boolean =
!constraint.contains(param) || {
@@ -126,14 +135,14 @@ trait ConstraintHandling {
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
val saved = frozenConstraint
- frozenConstraint = true
+ frozenConstraint = !alwaysFluid
try isSubType(tp1, tp2)
finally frozenConstraint = saved
}
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
val saved = frozenConstraint
- frozenConstraint = true
+ frozenConstraint = !alwaysFluid
try isSameType(tp1, tp2)
finally frozenConstraint = saved
}
@@ -219,7 +228,7 @@ trait ConstraintHandling {
// is not a union type, approximate the union type from above by an intersection
// of all common base types.
if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound))
- inst = inst.approximateUnion
+ inst = ctx.harmonizeUnion(inst)
// 3. If instance is from below, and upper bound has open named parameters
// make sure the instance has all named parameters of the bound.
@@ -307,12 +316,12 @@ trait ConstraintHandling {
* missing.
*/
def pruneLambdaParams(tp: Type) =
- if (comparingLambdas && param.binder.isInstanceOf[PolyType]) {
+ if (comparedPolyTypes.nonEmpty) {
val approx = new ApproximatingTypeMap {
def apply(t: Type): Type = t match {
- case t @ PolyParam(tl: TypeLambda, n) =>
+ case t @ PolyParam(pt: PolyType, n) if comparedPolyTypes contains pt =>
val effectiveVariance = if (fromBelow) -variance else variance
- val bounds = tl.paramBounds(n)
+ val bounds = pt.paramBounds(n)
if (effectiveVariance > 0) bounds.lo
else if (effectiveVariance < 0) bounds.hi
else NoType
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index 5c9fdaf88..edc68588d 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -132,7 +132,7 @@ object Contexts {
def compilationUnit: CompilationUnit = _compilationUnit
/** The current tree */
- private[this] var _tree: Tree[_ >: Untyped] = _
+ private[this] var _tree: Tree[_ >: Untyped]= _
protected def tree_=(tree: Tree[_ >: Untyped]) = _tree = tree
def tree: Tree[_ >: Untyped] = _tree
diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala
index 3bf17730a..b0f1f0c98 100644
--- a/src/dotty/tools/dotc/core/Decorators.scala
+++ b/src/dotty/tools/dotc/core/Decorators.scala
@@ -176,6 +176,10 @@ object Decorators {
*/
def ex(args: Any*)(implicit ctx: Context): String =
explained2(implicit ctx => em(args: _*))
+
+ /** Formatter that adds syntax highlighting to all interpolated values */
+ def hl(args: Any*)(implicit ctx: Context): String =
+ new SyntaxFormatter(sc).assemble(args)
}
}
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 75b75d3d5..50746c61d 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -92,17 +92,17 @@ class Definitions {
}
private def newPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int,
- resultTypeFn: GenericType => Type, flags: FlagSet = EmptyFlags) = {
+ resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = {
val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount)
val tparamBounds = tparamNames map (_ => TypeBounds.empty)
val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn)
newMethod(cls, name, ptype, flags)
}
- private def newT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: GenericType => Type, flags: FlagSet) =
+ private def newT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) =
newPolyMethod(cls, name, 1, resultTypeFn, flags)
- private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: GenericType => Type, flags: FlagSet) =
+ private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) =
newPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags)
private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[TypeRef] = {
@@ -430,10 +430,8 @@ class Definitions {
def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol
lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix)
def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol
- lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
+ lazy val LanguageModuleRef = ctx.requiredModule("scala.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
- lazy val Scala2LanguageModuleRef = ctx.requiredModule("scala.language")
- def Scala2LanguageModuleClass(implicit ctx: Context) = Scala2LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 4f01c43cf..7866d6697 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -6,6 +6,7 @@ import SymDenotations.{ SymDenotation, ClassDenotation, NoDenotation, NotDefined
import Contexts.{Context, ContextBase}
import Names.{Name, PreName}
import Names.TypeName
+import StdNames._
import Symbols.NoSymbol
import Symbols._
import Types._
@@ -247,6 +248,25 @@ object Denotations {
else asSingleDenotation
}
+ /** Handle merge conflict by throwing a `MergeError` exception */
+ private def mergeConflict(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
+ def showType(tp: Type) = tp match {
+ case ClassInfo(_, cls, _, _, _) => cls.showLocated
+ case bounds: TypeBounds => i"type bounds $bounds"
+ case _ => tp.show
+ }
+ if (true) throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2)
+ else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging
+ }
+
+ /** Merge two lists of names. If names in corresponding positions match, keep them,
+ * otherwise generate new synthetic names.
+ */
+ def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = {
+ for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped)
+ yield if (name1 == name2) name1 else syntheticName(idx)
+ }.toList
+
/** Form a denotation by conjoining with denotation `that`.
*
* NoDenotations are dropped. MultiDenotations are handled by merging
@@ -277,6 +297,50 @@ object Denotations {
*/
def & (that: Denotation, pre: Type, safeIntersection: Boolean = false)(implicit ctx: Context): Denotation = {
+ /** Normally, `tp1 & tp2`. Special cases for matching methods and classes, with
+ * the possibility of raising a merge error.
+ */
+ def infoMeet(tp1: Type, tp2: Type): Type = {
+ if (tp1 eq tp2) tp1
+ else tp1 match {
+ case tp1: TypeBounds =>
+ tp2 match {
+ case tp2: TypeBounds => if (safeIntersection) tp1 safe_& tp2 else tp1 & tp2
+ case tp2: ClassInfo if tp1 contains tp2 => tp2
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1: ClassInfo =>
+ tp2 match {
+ case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix)
+ case tp2: TypeBounds if tp2 contains tp1 => tp1
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1 @ MethodType(names1, formals1) if isTerm =>
+ tp2 match {
+ case tp2 @ MethodType(names2, formals2) if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
+ tp1.isImplicit == tp2.isImplicit =>
+ tp1.derivedMethodType(
+ mergeNames(names1, names2, nme.syntheticParamName),
+ formals1,
+ infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
+ case _ =>
+ mergeConflict(tp1, tp2)
+ }
+ case tp1: PolyType if isTerm =>
+ tp2 match {
+ case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
+ tp1.derivedPolyType(
+ mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
+ tp1.paramBounds,
+ infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
+ case _: MethodicType =>
+ mergeConflict(tp1, tp2)
+ }
+ case _ =>
+ tp1 & tp2
+ }
+ }
+
/** Try to merge denot1 and denot2 without adding a new signature. */
def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match {
case denot1 @ MultiDenotation(denot11, denot12) =>
@@ -289,95 +353,95 @@ object Denotations {
}
case denot1: SingleDenotation =>
if (denot1 eq denot2) denot1
- else if (denot1.matches(denot2)) {
- val info1 = denot1.info
- val info2 = denot2.info
- val sym1 = denot1.symbol
- val sym2 = denot2.symbol
-
- val sym2Accessible = sym2.isAccessibleFrom(pre)
-
- /** Does `sym1` come before `sym2` in the linearization of `pre`? */
- def precedes(sym1: Symbol, sym2: Symbol) = {
- def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
- case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
- case Nil => true
- }
- (sym1 ne sym2) &&
- (sym1.derivesFrom(sym2) ||
- !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses))
- }
+ else if (denot1.matches(denot2)) mergeSingleDenot(denot1, denot2)
+ else NoDenotation
+ }
- /** Similar to SymDenotation#accessBoundary, but without the special cases. */
- def accessBoundary(sym: Symbol) =
- if (sym.is(Private)) sym.owner
- else sym.privateWithin.orElse(
- if (sym.is(Protected)) sym.owner.enclosingPackageClass
- else defn.RootClass
- )
-
- /** Establish a partial order "preference" order between symbols.
- * Give preference to `sym1` over `sym2` if one of the following
- * conditions holds, in decreasing order of weight:
- * 1. sym1 is concrete and sym2 is abstract
- * 2. The owner of sym1 comes before the owner of sym2 in the linearization
- * of the type of the prefix `pre`.
- * 3. The access boundary of sym2 is properly contained in the access
- * boundary of sym1. For protected access, we count the enclosing
- * package as access boundary.
- * 4. sym1 a method but sym2 is not.
- * The aim of these criteria is to give some disambiguation on access which
- * - does not depend on textual order or other arbitrary choices
- * - minimizes raising of doubleDef errors
- */
- def preferSym(sym1: Symbol, sym2: Symbol) =
- sym1.eq(sym2) ||
- sym1.isAsConcrete(sym2) &&
- (!sym2.isAsConcrete(sym1) ||
- precedes(sym1.owner, sym2.owner) ||
- accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
- sym1.is(Method) && !sym2.is(Method))
-
- /** Sym preference provided types also override */
- def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
- preferSym(sym1, sym2) && info1.overrides(info2)
-
- def handleDoubleDef =
- if (preferSym(sym1, sym2)) denot1
- else if (preferSym(sym2, sym1)) denot2
- else doubleDefError(denot1, denot2, pre)
-
- if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
- else {
- val sym1Accessible = sym1.isAccessibleFrom(pre)
- if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
- else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
- else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
- else if (isDoubleDef(sym1, sym2)) handleDoubleDef
- else {
- val sym =
- if (!sym1.exists) sym2
- else if (!sym2.exists) sym1
- else if (preferSym(sym2, sym1)) sym2
- else sym1
- val jointInfo =
- try
- if (safeIntersection)
- info1 safe_& info2
- else
- info1 & info2
- catch {
- case ex: MergeError =>
- if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
- info1 // follow Scala2 linearization -
- // compare with way merge is performed in SymDenotation#computeMembersNamed
- else
- throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
- }
- new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
+ /** Try to merge single-denotations. */
+ def mergeSingleDenot(denot1: SingleDenotation, denot2: SingleDenotation): SingleDenotation = {
+ val info1 = denot1.info
+ val info2 = denot2.info
+ val sym1 = denot1.symbol
+ val sym2 = denot2.symbol
+
+ val sym2Accessible = sym2.isAccessibleFrom(pre)
+
+ /** Does `sym1` come before `sym2` in the linearization of `pre`? */
+ def precedes(sym1: Symbol, sym2: Symbol) = {
+ def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
+ case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
+ case Nil => true
+ }
+ (sym1 ne sym2) &&
+ (sym1.derivesFrom(sym2) ||
+ !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses))
+ }
+
+ /** Similar to SymDenotation#accessBoundary, but without the special cases. */
+ def accessBoundary(sym: Symbol) =
+ if (sym.is(Private)) sym.owner
+ else sym.privateWithin.orElse(
+ if (sym.is(Protected)) sym.owner.enclosingPackageClass
+ else defn.RootClass)
+
+ /** Establish a partial order "preference" order between symbols.
+ * Give preference to `sym1` over `sym2` if one of the following
+ * conditions holds, in decreasing order of weight:
+ * 1. sym1 is concrete and sym2 is abstract
+ * 2. The owner of sym1 comes before the owner of sym2 in the linearization
+ * of the type of the prefix `pre`.
+ * 3. The access boundary of sym2 is properly contained in the access
+ * boundary of sym1. For protected access, we count the enclosing
+ * package as access boundary.
+ * 4. sym1 a method but sym2 is not.
+ * The aim of these criteria is to give some disambiguation on access which
+ * - does not depend on textual order or other arbitrary choices
+ * - minimizes raising of doubleDef errors
+ */
+ def preferSym(sym1: Symbol, sym2: Symbol) =
+ sym1.eq(sym2) ||
+ sym1.isAsConcrete(sym2) &&
+ (!sym2.isAsConcrete(sym1) ||
+ precedes(sym1.owner, sym2.owner) ||
+ accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
+ sym1.is(Method) && !sym2.is(Method)) ||
+ sym1.info.isErroneous
+
+ /** Sym preference provided types also override */
+ def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
+ preferSym(sym1, sym2) && info1.overrides(info2)
+
+ def handleDoubleDef =
+ if (preferSym(sym1, sym2)) denot1
+ else if (preferSym(sym2, sym1)) denot2
+ else doubleDefError(denot1, denot2, pre)
+
+ if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
+ else {
+ val sym1Accessible = sym1.isAccessibleFrom(pre)
+ if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
+ else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
+ else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
+ else if (isDoubleDef(sym1, sym2)) handleDoubleDef
+ else {
+ val sym =
+ if (!sym1.exists) sym2
+ else if (!sym2.exists) sym1
+ else if (preferSym(sym2, sym1)) sym2
+ else sym1
+ val jointInfo =
+ try infoMeet(info1, info2)
+ catch {
+ case ex: MergeError =>
+ if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
+ info1 // follow Scala2 linearization -
+ // compare with way merge is performed in SymDenotation#computeMembersNamed
+ else
+ throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
}
- }
- } else NoDenotation
+ new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
+ }
+ }
}
if (this eq that) this
@@ -398,6 +462,46 @@ object Denotations {
*/
def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
+ /** Normally, `tp1 | tp2`. Special cases for matching methods and classes, with
+ * the possibility of raising a merge error.
+ */
+ def infoJoin(tp1: Type, tp2: Type): Type = tp1 match {
+ case tp1: TypeBounds =>
+ tp2 match {
+ case tp2: TypeBounds => tp1 | tp2
+ case tp2: ClassInfo if tp1 contains tp2 => tp1
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1: ClassInfo =>
+ tp2 match {
+ case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix)
+ case tp2: TypeBounds if tp2 contains tp1 => tp2
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1 @ MethodType(names1, formals1) =>
+ tp2 match {
+ case tp2 @ MethodType(names2, formals2)
+ if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
+ tp1.isImplicit == tp2.isImplicit =>
+ tp1.derivedMethodType(
+ mergeNames(names1, names2, nme.syntheticParamName),
+ formals1, tp1.resultType | tp2.resultType.subst(tp2, tp1))
+ case _ =>
+ mergeConflict(tp1, tp2)
+ }
+ case tp1: PolyType =>
+ tp2 match {
+ case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
+ tp1.derivedPolyType(
+ mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
+ tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1))
+ case _ =>
+ mergeConflict(tp1, tp2)
+ }
+ case _ =>
+ tp1 | tp2
+ }
+
def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation =
if (denot1.matches(denot2)) {
val sym1 = denot1.symbol
@@ -427,7 +531,8 @@ object Denotations {
}
lubSym(sym1.allOverriddenSymbols, NoSymbol)
}
- new JointRefDenotation(jointSym, info1 | info2, denot1.validFor & denot2.validFor)
+ new JointRefDenotation(
+ jointSym, infoJoin(info1, info2), denot1.validFor & denot2.validFor)
}
}
else NoDenotation
@@ -1133,5 +1238,4 @@ object Denotations {
util.Stats.record("not defined here")
override def getMessage() = msg
}
-}
-
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/core/OrderingConstraint.scala b/src/dotty/tools/dotc/core/OrderingConstraint.scala
index 458f8b82f..72c7a8e51 100644
--- a/src/dotty/tools/dotc/core/OrderingConstraint.scala
+++ b/src/dotty/tools/dotc/core/OrderingConstraint.scala
@@ -14,7 +14,7 @@ import annotation.tailrec
object OrderingConstraint {
- type ArrayValuedMap[T] = SimpleMap[GenericType, Array[T]]
+ type ArrayValuedMap[T] = SimpleMap[PolyType, Array[T]]
/** The type of `OrderingConstraint#boundsMap` */
type ParamBounds = ArrayValuedMap[Type]
@@ -32,11 +32,11 @@ object OrderingConstraint {
/** A lens for updating a single entry array in one of the three constraint maps */
abstract class ConstraintLens[T <: AnyRef: ClassTag] {
- def entries(c: OrderingConstraint, poly: GenericType): Array[T]
- def updateEntries(c: OrderingConstraint, poly: GenericType, entries: Array[T])(implicit ctx: Context): OrderingConstraint
+ def entries(c: OrderingConstraint, poly: PolyType): Array[T]
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[T])(implicit ctx: Context): OrderingConstraint
def initial: T
- def apply(c: OrderingConstraint, poly: GenericType, idx: Int) = {
+ def apply(c: OrderingConstraint, poly: PolyType, idx: Int) = {
val es = entries(c, poly)
if (es == null) initial else es(idx)
}
@@ -47,7 +47,7 @@ object OrderingConstraint {
* parts of `current` which are not shared by `prev`.
*/
def update(prev: OrderingConstraint, current: OrderingConstraint,
- poly: GenericType, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = {
+ poly: PolyType, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = {
var es = entries(current, poly)
if (es != null && (es(idx) eq entry)) current
else {
@@ -72,7 +72,7 @@ object OrderingConstraint {
update(prev, current, param.binder, param.paramNum, entry)
def map(prev: OrderingConstraint, current: OrderingConstraint,
- poly: GenericType, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint =
+ poly: PolyType, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint =
update(prev, current, poly, idx, f(apply(current, poly, idx)))
def map(prev: OrderingConstraint, current: OrderingConstraint,
@@ -81,25 +81,25 @@ object OrderingConstraint {
}
val boundsLens = new ConstraintLens[Type] {
- def entries(c: OrderingConstraint, poly: GenericType): Array[Type] =
+ def entries(c: OrderingConstraint, poly: PolyType): Array[Type] =
c.boundsMap(poly)
- def updateEntries(c: OrderingConstraint, poly: GenericType, entries: Array[Type])(implicit ctx: Context): OrderingConstraint =
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[Type])(implicit ctx: Context): OrderingConstraint =
newConstraint(c.boundsMap.updated(poly, entries), c.lowerMap, c.upperMap)
def initial = NoType
}
val lowerLens = new ConstraintLens[List[PolyParam]] {
- def entries(c: OrderingConstraint, poly: GenericType): Array[List[PolyParam]] =
+ def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] =
c.lowerMap(poly)
- def updateEntries(c: OrderingConstraint, poly: GenericType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint =
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint =
newConstraint(c.boundsMap, c.lowerMap.updated(poly, entries), c.upperMap)
def initial = Nil
}
val upperLens = new ConstraintLens[List[PolyParam]] {
- def entries(c: OrderingConstraint, poly: GenericType): Array[List[PolyParam]] =
+ def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] =
c.upperMap(poly)
- def updateEntries(c: OrderingConstraint, poly: GenericType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint =
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint =
newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries))
def initial = Nil
}
@@ -149,7 +149,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
// ----------- Contains tests --------------------------------------------------
- def contains(pt: GenericType): Boolean = boundsMap(pt) != null
+ def contains(pt: PolyType): Boolean = boundsMap(pt) != null
def contains(param: PolyParam): Boolean = {
val entries = boundsMap(param.binder)
@@ -280,7 +280,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
stripParams(tp, paramBuf, isUpper)
.orElse(if (isUpper) defn.AnyType else defn.NothingType)
- def add(poly: GenericType, tvars: List[TypeVar])(implicit ctx: Context): This = {
+ def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This = {
assert(!contains(poly))
val nparams = poly.paramNames.length
val entries1 = new Array[Type](nparams * 2)
@@ -293,7 +293,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
* Update all bounds to be normalized and update ordering to account for
* dependent parameters.
*/
- private def init(poly: GenericType)(implicit ctx: Context): This = {
+ private def init(poly: PolyType)(implicit ctx: Context): This = {
var current = this
val loBuf, hiBuf = new mutable.ListBuffer[PolyParam]
var i = 0
@@ -393,14 +393,14 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
val replacement = tp.dealias.stripTypeVar
if (param == replacement) this
else {
- assert(replacement.isValueType)
+ assert(replacement.isValueTypeOrLambda)
val poly = param.binder
val idx = param.paramNum
def removeParam(ps: List[PolyParam]) =
ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx)
- def replaceParam(tp: Type, atPoly: GenericType, atIdx: Int): Type = tp match {
+ def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int): Type = tp match {
case bounds @ TypeBounds(lo, hi) =>
def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = {
@@ -440,9 +440,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
}
}
- def remove(pt: GenericType)(implicit ctx: Context): This = {
+ def remove(pt: PolyType)(implicit ctx: Context): This = {
def removeFromOrdering(po: ParamOrdering) = {
- def removeFromBoundss(key: GenericType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = {
+ def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = {
val bndss1 = bndss.map(_.filterConserve(_.binder ne pt))
if (bndss.corresponds(bndss1)(_ eq _)) bndss else bndss1
}
@@ -451,7 +451,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap))
}
- def isRemovable(pt: GenericType): Boolean = {
+ def isRemovable(pt: PolyType): Boolean = {
val entries = boundsMap(pt)
@tailrec def allRemovable(last: Int): Boolean =
if (last < 0) true
@@ -464,7 +464,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
// ---------- Exploration --------------------------------------------------------
- def domainPolys: List[GenericType] = boundsMap.keys
+ def domainPolys: List[PolyType] = boundsMap.keys
def domainParams: List[PolyParam] =
for {
@@ -481,7 +481,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
true
}
- def foreachParam(p: (GenericType, Int) => Unit): Unit =
+ def foreachParam(p: (PolyType, Int) => Unit): Unit =
boundsMap.foreachBinding { (poly, entries) =>
0.until(poly.paramNames.length).foreach(p(poly, _))
}
@@ -521,6 +521,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
case _ if e1 contains e2 => e2
case _ => mergeError
}
+ case tv1: TypeVar =>
+ e2 match {
+ case tv2: TypeVar if tv1.instanceOpt eq tv2.instanceOpt => e1
+ case _ => mergeError
+ }
case _ if e1 eq e2 => e1
case _ => mergeError
}
@@ -536,7 +541,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
override def checkClosed()(implicit ctx: Context): Unit = {
def isFreePolyParam(tp: Type) = tp match {
- case PolyParam(binder: GenericType, _) => !contains(binder)
+ case PolyParam(binder: PolyType, _) => !contains(binder)
case _ => false
}
def checkClosedType(tp: Type, where: String) =
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index c52264637..920c9635e 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -429,7 +429,6 @@ object StdNames {
val isEmpty: N = "isEmpty"
val isInstanceOf_ : N = "isInstanceOf"
val java: N = "java"
- val keepUnions: N = "keepUnions"
val key: N = "key"
val lang: N = "lang"
val length: N = "length"
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 5a5eacd18..a98d6732a 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -41,7 +41,7 @@ trait SymDenotations { this: Context =>
}
def stillValid(denot: SymDenotation): Boolean =
- if (denot.is(ValidForever) || denot.isRefinementClass) true
+ if (denot.is(ValidForever) || denot.isRefinementClass || denot.isImport) true
else {
val initial = denot.initial
val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id)
@@ -590,6 +590,9 @@ object SymDenotations {
originalName.isSetterName &&
(!isCompleted || info.firstParamTypes.nonEmpty) // to avoid being fooled by var x_= : Unit = ...
+ /** is this a symbol representing an import? */
+ final def isImport = name == nme.IMPORT
+
/** is this the constructor of a class? */
final def isClassConstructor = name == nme.CONSTRUCTOR
@@ -1147,7 +1150,7 @@ object SymDenotations {
case tp: NamedType => hasSkolems(tp.prefix)
case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo)
case tp: RecType => hasSkolems(tp.parent)
- case tp: GenericType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType)
+ case tp: PolyType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType)
case tp: MethodType => tp.paramTypes.exists(hasSkolems) || hasSkolems(tp.resType)
case tp: ExprType => hasSkolems(tp.resType)
case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems)
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index f32a591a6..8aaf77032 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -72,7 +72,7 @@ object TypeApplications {
}
def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match {
- case tp @ TypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn)
+ case tp @ PolyType(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn)
case _ => None
}
}
@@ -159,7 +159,7 @@ object TypeApplications {
* result type. Using this mode, we can guarantee that `appliedTo` will never
* produce a higher-kinded application with a type lambda as type constructor.
*/
- class Reducer(tycon: TypeLambda, args: List[Type])(implicit ctx: Context) extends TypeMap {
+ class Reducer(tycon: PolyType, args: List[Type])(implicit ctx: Context) extends TypeMap {
private var available = (0 until args.length).toSet
var allReplaced = true
def hasWildcardArg(p: PolyParam) =
@@ -212,7 +212,7 @@ class TypeApplications(val self: Type) extends AnyVal {
self match {
case self: ClassInfo =>
self.cls.typeParams
- case self: TypeLambda =>
+ case self: PolyType =>
self.typeParams
case self: TypeRef =>
val tsym = self.symbol
@@ -311,7 +311,7 @@ class TypeApplications(val self: Type) extends AnyVal {
def isHK(implicit ctx: Context): Boolean = self.dealias match {
case self: TypeRef => self.info.isHK
case self: RefinedType => false
- case self: TypeLambda => true
+ case self: PolyType => true
case self: SingletonType => false
case self: TypeVar =>
// Using `origin` instead of `underlying`, as is done for typeParams,
@@ -339,7 +339,7 @@ class TypeApplications(val self: Type) extends AnyVal {
*/
def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = {
def expand(tp: Type) =
- TypeLambda(
+ PolyType(
tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.paramVariance))(
tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
tl => tl.lifted(tparams, tp))
@@ -421,10 +421,10 @@ class TypeApplications(val self: Type) extends AnyVal {
if (hkParams.isEmpty) self
else {
def adaptArg(arg: Type): Type = arg match {
- case arg @ TypeLambda(tparams, body) if
+ case arg @ PolyType(tparams, body) if
!tparams.corresponds(hkParams)(_.paramVariance == _.paramVariance) &&
tparams.corresponds(hkParams)(varianceConforms) =>
- TypeLambda(tparams.map(_.paramName), hkParams.map(_.paramVariance))(
+ PolyType(tparams.map(_.paramName), hkParams.map(_.paramVariance))(
tl => arg.paramBounds.map(_.subst(arg, tl).bounds),
tl => arg.resultType.subst(arg, tl)
)
@@ -466,7 +466,7 @@ class TypeApplications(val self: Type) extends AnyVal {
val dealiased = stripped.safeDealias
if (args.isEmpty || ctx.erasedTypes) self
else dealiased match {
- case dealiased: TypeLambda =>
+ case dealiased: PolyType =>
def tryReduce =
if (!args.exists(_.isInstanceOf[TypeBounds])) {
val followAlias = Config.simplifyApplications && {
@@ -485,7 +485,7 @@ class TypeApplications(val self: Type) extends AnyVal {
// In this case we should always dealias since we cannot handle
// higher-kinded applications to wildcard arguments.
dealiased
- .derivedTypeLambda(resType = tycon.safeDealias.appliedTo(args1))
+ .derivedPolyType(resType = tycon.safeDealias.appliedTo(args1))
.appliedTo(args)
case _ =>
val reducer = new Reducer(dealiased, args)
@@ -494,8 +494,6 @@ class TypeApplications(val self: Type) extends AnyVal {
else HKApply(dealiased, args)
}
tryReduce
- case dealiased: PolyType =>
- dealiased.instantiate(args)
case dealiased: AndOrType =>
dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args))
case dealiased: TypeAlias =>
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 991dd2664..1980fe50d 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -8,7 +8,7 @@ import StdNames.{nme, tpnme}
import collection.mutable
import util.{Stats, DotClass, SimpleMap}
import config.Config
-import config.Printers.{typr, constr, subtyping}
+import config.Printers.{typr, constr, subtyping, noPrinter}
import TypeErasure.{erasedLub, erasedGlb}
import TypeApplications._
import scala.util.control.NonFatal
@@ -324,8 +324,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case AndType(tp11, tp12) =>
if (tp11.stripTypeVar eq tp12.stripTypeVar) isSubType(tp11, tp2)
else thirdTry(tp1, tp2)
- case OrType(tp11, tp12) =>
- isSubType(tp11, tp2) && isSubType(tp12, tp2)
+ case tp1 @ OrType(tp11, tp12) =>
+ def joinOK = tp2.dealias match {
+ case tp12: HKApply =>
+ // If we apply the default algorithm for `A[X] | B[Y] <: C[Z]` where `C` is a
+ // type parameter, we will instantiate `C` to `A` and then fail when comparing
+ // with `B[Y]`. To do the right thing, we need to instantiate `C` to the
+ // common superclass of `A` and `B`.
+ isSubType(tp1.join, tp2)
+ case _ =>
+ false
+ }
+ joinOK || isSubType(tp11, tp2) && isSubType(tp12, tp2)
case ErrorType =>
true
case _ =>
@@ -402,9 +412,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
compareRec
case tp2 @ HKApply(tycon2, args2) =>
compareHkApply2(tp1, tp2, tycon2, args2)
- case tp2 @ TypeLambda(tparams2, body2) =>
+ case tp2 @ PolyType(tparams2, body2) =>
def compareHkLambda: Boolean = tp1.stripTypeVar match {
- case tp1 @ TypeLambda(tparams1, body1) =>
+ case tp1 @ PolyType(tparams1, body1) =>
/* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail
* The issue is that, logically, bounds should compare contravariantly,
* but that would invalidate a pattern exploited in t2994:
@@ -422,13 +432,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
ctx.scala2Mode ||
tparams1.corresponds(tparams2)((tparam1, tparam2) =>
isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds))
- val saved = comparingLambdas
- comparingLambdas = true
+ val saved = comparedPolyTypes
+ comparedPolyTypes += tp1
+ comparedPolyTypes += tp2
try
variancesConform(tparams1, tparams2) &&
boundsOK &&
isSubType(body1, body2.subst(tp2, tp1))
- finally comparingLambdas = saved
+ finally comparedPolyTypes = saved
case _ =>
if (!tp1.isHK) {
tp2 match {
@@ -468,16 +479,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
false
}
compareMethod
- case tp2: PolyType =>
- def comparePoly = tp1 match {
- case tp1: PolyType =>
- (tp1.signature consistentParams tp2.signature) &&
- matchingTypeParams(tp1, tp2) &&
- isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1))
- case _ =>
- false
- }
- comparePoly
case tp2 @ ExprType(restpe2) =>
def compareExpr = tp1 match {
// We allow ()T to be a subtype of => T.
@@ -533,7 +534,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
case _ => false
}
- (tp1.symbol eq NothingClass) && tp2.isInstanceOf[ValueType] ||
+ (tp1.symbol eq NothingClass) && tp2.isValueTypeOrLambda ||
(tp1.symbol eq NullClass) && isNullable(tp2)
}
case tp1: SingletonType =>
@@ -650,7 +651,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
val tparams1 = tparams1a.drop(lengthDiff)
variancesConform(tparams1, tparams) && {
if (lengthDiff > 0)
- tycon1b = TypeLambda(tparams1.map(_.paramName), tparams1.map(_.paramVariance))(
+ tycon1b = PolyType(tparams1.map(_.paramName), tparams1.map(_.paramVariance))(
tl => tparams1.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
tl => tycon1a.appliedTo(args1.take(lengthDiff) ++
tparams1.indices.toList.map(PolyParam(tl, _))))
@@ -827,8 +828,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
op1 && {
val leftConstraint = constraint
constraint = preConstraint
- if (!(op2 && subsumes(leftConstraint, constraint, preConstraint)))
+ if (!(op2 && subsumes(leftConstraint, constraint, preConstraint))) {
+ if (constr != noPrinter && !subsumes(constraint, leftConstraint, preConstraint))
+ constr.println(i"CUT - prefer $leftConstraint over $constraint")
constraint = leftConstraint
+ }
true
} || op2
}
@@ -961,28 +965,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* Test that the resulting bounds are still satisfiable.
*/
private def narrowGADTBounds(tr: NamedType, bound: Type, isUpper: Boolean): Boolean =
- ctx.mode.is(Mode.GADTflexible) && {
- val tparam = tr.symbol
- typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}")
- if (bound.isRef(tparam)) false
- else bound match {
- case bound: TypeRef
- if bound.symbol.is(BindDefinedType) && ctx.gadt.bounds.contains(bound.symbol) &&
- !tr.symbol.is(BindDefinedType) =>
- // Avoid having pattern-bound types in gadt bounds,
- // as these might be eliminated once the pattern is typechecked.
- // Pattern-bound type symbols should be narrowed first, only if that fails
- // should symbols in the environment be constrained.
- narrowGADTBounds(bound, tr, !isUpper)
- case _ =>
- val oldBounds = ctx.gadt.bounds(tparam)
- val newBounds =
- if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound)
- else TypeBounds(oldBounds.lo | bound, oldBounds.hi)
- isSubType(newBounds.lo, newBounds.hi) &&
- { ctx.gadt.setBounds(tparam, newBounds); true }
+ ctx.mode.is(Mode.GADTflexible) && !frozenConstraint && {
+ val tparam = tr.symbol
+ typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}")
+ if (bound.isRef(tparam)) false
+ else bound match {
+ case bound: TypeRef
+ if bound.symbol.is(BindDefinedType) &&
+ ctx.gadt.bounds.contains(bound.symbol) &&
+ !tr.symbol.is(BindDefinedType) =>
+ // Avoid having pattern-bound types in gadt bounds,
+ // as these might be eliminated once the pattern is typechecked.
+ // Pattern-bound type symbols should be narrowed first, only if that fails
+ // should symbols in the environment be constrained.
+ narrowGADTBounds(bound, tr, !isUpper)
+ case _ =>
+ val oldBounds = ctx.gadt.bounds(tparam)
+ val newBounds =
+ if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound)
+ else TypeBounds(oldBounds.lo | bound, oldBounds.hi)
+ isSubType(newBounds.lo, newBounds.hi) &&
+ { ctx.gadt.setBounds(tparam, newBounds); true }
+ }
}
- }
// Tests around `matches`
@@ -1019,7 +1024,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
}
/** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */
- private def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match {
+ def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match {
case formal1 :: rest1 =>
formals2 match {
case formal2 :: rest2 =>
@@ -1037,7 +1042,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
/** Do generic types `poly1` and `poly2` have type parameters that
* have the same bounds (after renaming one set to the other)?
*/
- private def matchingTypeParams(poly1: GenericType, poly2: GenericType): Boolean =
+ def matchingTypeParams(poly1: PolyType, poly2: PolyType): Boolean =
(poly1.paramBounds corresponds poly2.paramBounds)((b1, b2) =>
isSameType(b1, b2.subst(poly2, poly1)))
@@ -1261,7 +1266,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
else if (tparams2.isEmpty)
original(tp1.appliedTo(tp1.typeParams.map(_.paramBoundsAsSeenFrom(tp1))), tp2)
else
- TypeLambda(
+ PolyType(
paramNames = tpnme.syntheticLambdaParamNames(tparams1.length),
variances = (tparams1, tparams2).zipped.map((tparam1, tparam2) =>
(tparam1.paramVariance + tparam2.paramVariance) / 2))(
@@ -1305,38 +1310,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
}
case tp1: RecType =>
tp1.rebind(distributeAnd(tp1.parent, tp2))
- case tp1: TypeBounds =>
- tp2 match {
- case tp2: TypeBounds => tp1 & tp2
- case tp2: ClassInfo if tp1 contains tp2 => tp2
- case _ => mergeConflict(tp1, tp2)
- }
- case tp1: ClassInfo =>
- tp2 match {
- case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix)
- case tp2: TypeBounds if tp2 contains tp1 => tp1
- case _ => mergeConflict(tp1, tp2)
- }
- case tp1 @ MethodType(names1, formals1) =>
- tp2 match {
- case tp2 @ MethodType(names2, formals2)
- if matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
- tp1.isImplicit == tp2.isImplicit =>
- tp1.derivedMethodType(
- mergeNames(names1, names2, nme.syntheticParamName),
- formals1, tp1.resultType & tp2.resultType.subst(tp2, tp1))
- case _ =>
- mergeConflict(tp1, tp2)
- }
- case tp1: PolyType =>
- tp2 match {
- case tp2: PolyType if matchingTypeParams(tp1, tp2) =>
- tp1.derivedPolyType(
- mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
- tp1.paramBounds, tp1.resultType & tp2.resultType.subst(tp2, tp1))
- case _ =>
- mergeConflict(tp1, tp2)
- }
case ExprType(rt1) =>
tp2 match {
case ExprType(rt2) =>
@@ -1361,38 +1334,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* The rhs is a proper supertype of the lhs.
*/
private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match {
- case tp1: TypeBounds =>
- tp2 match {
- case tp2: TypeBounds => tp1 | tp2
- case tp2: ClassInfo if tp1 contains tp2 => tp1
- case _ => mergeConflict(tp1, tp2)
- }
- case tp1: ClassInfo =>
- tp2 match {
- case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix)
- case tp2: TypeBounds if tp2 contains tp1 => tp2
- case _ => mergeConflict(tp1, tp2)
- }
- case tp1 @ MethodType(names1, formals1) =>
- tp2 match {
- case tp2 @ MethodType(names2, formals2)
- if matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
- tp1.isImplicit == tp2.isImplicit =>
- tp1.derivedMethodType(
- mergeNames(names1, names2, nme.syntheticParamName),
- formals1, tp1.resultType | tp2.resultType.subst(tp2, tp1))
- case _ =>
- mergeConflict(tp1, tp2)
- }
- case tp1: GenericType =>
- tp2 match {
- case tp2: GenericType if matchingTypeParams(tp1, tp2) =>
- tp1.derivedGenericType(
- mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
- tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1))
- case _ =>
- mergeConflict(tp1, tp2)
- }
case ExprType(rt1) =>
ExprType(rt1 | tp2.widenExpr)
case tp1: TypeVar if tp1.isInstantiated =>
@@ -1403,25 +1344,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
NoType
}
- /** Handle merge conflict by throwing a `MergeError` exception */
- private def mergeConflict(tp1: Type, tp2: Type): Type = {
- def showType(tp: Type) = tp match {
- case ClassInfo(_, cls, _, _, _) => cls.showLocated
- case bounds: TypeBounds => i"type bounds $bounds"
- case _ => tp.show
- }
- if (true) throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2)
- else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging
- }
-
- /** Merge two lists of names. If names in corresponding positions match, keep them,
- * otherwise generate new synthetic names.
- */
- private def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = {
- for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped)
- yield if (name1 == name2) name1 else syntheticName(idx)
- }.toList
-
/** Show type, handling type types better than the default */
private def showType(tp: Type)(implicit ctx: Context) = tp match {
case ClassInfo(_, cls, _, _, _) => cls.showLocated
diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala
index 1a7342a12..254ea3277 100644
--- a/src/dotty/tools/dotc/core/TypeErasure.scala
+++ b/src/dotty/tools/dotc/core/TypeErasure.scala
@@ -356,8 +356,6 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
SuperType(this(thistpe), this(supertpe))
case ExprType(rt) =>
defn.FunctionClass(0).typeRef
- case tp: TypeProxy =>
- this(tp.underlying)
case AndType(tp1, tp2) =>
erasedGlb(this(tp1), this(tp2), isJava)
case OrType(tp1, tp2) =>
@@ -372,11 +370,6 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
case rt =>
tp.derivedMethodType(tp.paramNames, formals, rt)
}
- case tp: PolyType =>
- this(tp.resultType) match {
- case rt: MethodType => rt
- case rt => MethodType(Nil, Nil, rt)
- }
case tp @ ClassInfo(pre, cls, classParents, decls, _) =>
if (cls is Package) tp
else {
@@ -398,6 +391,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
tp
case tp: WildcardType if wildcardOK =>
tp
+ case tp: TypeProxy =>
+ this(tp.underlying)
}
private def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
@@ -409,9 +404,9 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
else JavaArrayType(arrayErasure(elemtp))
}
- /** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s are
- * treated. `eraseInfo` maps them them to nullary method types, whereas `apply` maps them
- * to `Function0`.
+ /** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s and
+ * `PolyType`s are treated. `eraseInfo` maps them them to method types, whereas `apply` maps them
+ * to the underlying type.
*/
def eraseInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match {
case ExprType(rt) =>
@@ -421,6 +416,11 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
// forwarders to mixin methods.
// See doc comment for ElimByName for speculation how we could improve this.
else MethodType(Nil, Nil, eraseResult(rt))
+ case tp: PolyType =>
+ eraseResult(tp.resultType) match {
+ case rt: MethodType => rt
+ case rt => MethodType(Nil, Nil, rt)
+ }
case tp => this(tp)
}
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index 5ba9a3351..92e5f9d57 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -36,7 +36,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
* Instead we produce an annotated type that marks the prefix as unsafe:
*
* (x: (C @ UnsafeNonvariant)#T)C#T
-
+ *
* We also set a global state flag `unsafeNonvariant` to the current run.
* When typing a Select node, typer will check that flag, and if it
* points to the current run will scan the result type of the select for
@@ -136,7 +136,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
finally seen = saved
}
case _ =>
- if (tp.isInstanceOf[MethodicType]) assert(variance != 0, tp)
mapOver(tp)
}
}
@@ -173,21 +172,53 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
}
/** Approximate union type by intersection of its dominators.
- * See Type#approximateUnion for an explanation.
+ * That is, replace a union type Tn | ... | Tn
+ * by the smallest intersection type of base-class instances of T1,...,Tn.
+ * Example: Given
+ *
+ * trait C[+T]
+ * trait D
+ * class A extends C[A] with D
+ * class B extends C[B] with D with E
+ *
+ * we approximate `A | B` by `C[A | B] with D`
*/
- def approximateUnion(tp: Type): Type = {
+ def orDominator(tp: Type): Type = {
+
/** a faster version of cs1 intersect cs2 */
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
val cs2AsSet = new util.HashSet[ClassSymbol](100)
cs2.foreach(cs2AsSet.addEntry)
cs1.filter(cs2AsSet.contains)
}
+
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match {
case c :: rest =>
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
}
+
+ def mergeRefined(tp1: Type, tp2: Type): Type = {
+ def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2")
+ tp1 match {
+ case tp1 @ RefinedType(parent1, name1, rinfo1) =>
+ tp2 match {
+ case RefinedType(parent2, `name1`, rinfo2) =>
+ tp1.derivedRefinedType(
+ mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2)
+ case _ => fail
+ }
+ case tp1 @ TypeRef(pre1, name1) =>
+ tp2 match {
+ case tp2 @ TypeRef(pre2, `name1`) =>
+ tp1.derivedSelect(pre1 | pre2)
+ case _ => fail
+ }
+ case _ => fail
+ }
+ }
+
def approximateOr(tp1: Type, tp2: Type): Type = {
def isClassRef(tp: Type): Boolean = tp match {
case tp: TypeRef => tp.symbol.isClass
@@ -195,78 +226,70 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
case _ => false
}
- /** If `tp1` and `tp2` are typebounds, try to make one fit into the other
- * or to make them equal, by instantiating uninstantiated type variables.
- */
- def homogenizedUnion(tp1: Type, tp2: Type): Type = {
- tp1 match {
- case tp1: TypeBounds =>
- tp2 match {
- case tp2: TypeBounds =>
- def fitInto(tp1: TypeBounds, tp2: TypeBounds): Unit = {
- val nestedCtx = ctx.fresh.setNewTyperState
- if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx))
- nestedCtx.typerState.commit()
- }
- fitInto(tp1, tp2)
- fitInto(tp2, tp1)
- case _ =>
- }
- case _ =>
- }
- tp1 | tp2
- }
-
- tp1 match {
- case tp1: RefinedType =>
- tp2 match {
- case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
- return tp1.derivedRefinedType(
- approximateUnion(OrType(tp1.parent, tp2.parent)),
- tp1.refinedName,
- homogenizedUnion(tp1.refinedInfo, tp2.refinedInfo))
- //.ensuring { x => println(i"approx or $tp1 | $tp2 = $x\n constr = ${ctx.typerState.constraint}"); true } // DEBUG
- case _ =>
- }
- case _ =>
- }
-
tp1 match {
case tp1: RecType =>
tp1.rebind(approximateOr(tp1.parent, tp2))
case tp1: TypeProxy if !isClassRef(tp1) =>
- approximateUnion(tp1.superType | tp2)
+ orDominator(tp1.superType | tp2)
case _ =>
tp2 match {
case tp2: RecType =>
tp2.rebind(approximateOr(tp1, tp2.parent))
case tp2: TypeProxy if !isClassRef(tp2) =>
- approximateUnion(tp1 | tp2.superType)
+ orDominator(tp1 | tp2.superType)
case _ =>
val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
val doms = dominators(commonBaseClasses, Nil)
- def baseTp(cls: ClassSymbol): Type =
- if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
- else tp.baseTypeWithArgs(cls)
+ def baseTp(cls: ClassSymbol): Type = {
+ val base =
+ if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
+ else tp.baseTypeWithArgs(cls)
+ base.mapReduceOr(identity)(mergeRefined)
+ }
doms.map(baseTp).reduceLeft(AndType.apply)
}
}
}
- if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp
- else tp match {
+
+ tp match {
case tp: OrType =>
- approximateOr(tp.tp1, tp.tp2) // Maybe refactor using liftToRec?
- case tp @ AndType(tp1, tp2) =>
- tp derived_& (approximateUnion(tp1), approximateUnion(tp2))
- case tp: RefinedType =>
- tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo)
- case tp: RecType =>
- tp.rebind(approximateUnion(tp.parent))
+ approximateOr(tp.tp1, tp.tp2)
case _ =>
tp
}
}
+ /** Given a disjunction T1 | ... | Tn of types with potentially embedded
+ * type variables, constrain type variables further if this eliminates
+ * some of the branches of the disjunction. Do this also for disjunctions
+ * embedded in intersections, as parents in refinements, and in recursive types.
+ *
+ * For instance, if `A` is an unconstrained type variable, then
+ *
+ * ArrayBuffer[Int] | ArrayBuffer[A]
+ *
+ * is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
+ * instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
+ */
+ def harmonizeUnion(tp: Type): Type = tp match {
+ case tp: OrType =>
+ joinIfScala2(typeComparer.fluidly(tp.tp1 | tp.tp2))
+ case tp @ AndType(tp1, tp2) =>
+ tp derived_& (harmonizeUnion(tp1), harmonizeUnion(tp2))
+ case tp: RefinedType =>
+ tp.derivedRefinedType(harmonizeUnion(tp.parent), tp.refinedName, tp.refinedInfo)
+ case tp: RecType =>
+ tp.rebind(harmonizeUnion(tp.parent))
+ case _ =>
+ tp
+ }
+
+ /** Under -language:Scala2: Replace or-types with their joins */
+ private def joinIfScala2(tp: Type) = tp match {
+ case tp: OrType if scala2Mode => tp.join
+ case _ => tp
+ }
+
/** Not currently needed:
*
def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = {
@@ -493,7 +516,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
*/
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
def toPrefix(sym: Symbol): String =
- if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleClass)) ""
+ if (!sym.exists || (sym eq defn.LanguageModuleClass)) ""
else toPrefix(sym.owner) + sym.name + "."
def featureName = toPrefix(owner) + feature
def hasImport(implicit ctx: Context): Boolean = {
@@ -512,13 +535,13 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
/** Is auto-tupling enabled? */
def canAutoTuple =
- !featureEnabled(defn.Scala2LanguageModuleClass, nme.noAutoTupling)
+ !featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling)
def scala2Mode =
featureEnabled(defn.LanguageModuleClass, nme.Scala2)
def dynamicsEnabled =
- featureEnabled(defn.Scala2LanguageModuleClass, nme.dynamics)
+ featureEnabled(defn.LanguageModuleClass, nme.dynamics)
def testScala2Mode(msg: String, pos: Position) = {
if (scala2Mode) migrationWarning(msg, pos)
diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala
index 7e332b412..5c476c1cb 100644
--- a/src/dotty/tools/dotc/core/TyperState.scala
+++ b/src/dotty/tools/dotc/core/TyperState.scala
@@ -152,7 +152,7 @@ extends TyperState(r) {
}
override def gc()(implicit ctx: Context): Unit = {
- val toCollect = new mutable.ListBuffer[GenericType]
+ val toCollect = new mutable.ListBuffer[PolyType]
constraint foreachTypeVar { tvar =>
if (!tvar.inst.exists) {
val inst = instType(tvar)
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 2f1b6b829..38913a7d0 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -61,14 +61,13 @@ object Types {
* | +- ExprType
* | +- AnnotatedType
* | +- TypeVar
+ * | +- PolyType
* |
* +- GroundType -+- AndType
* +- OrType
* +- MethodType -----+- ImplicitMethodType
* | +- JavaMethodType
* +- ClassInfo
- * +- GenericType ----+- PolyType
- * | +- TypeLambda
* |
* +- NoType
* +- NoPrefix
@@ -96,6 +95,9 @@ object Types {
/** Is this type a value type? */
final def isValueType: Boolean = this.isInstanceOf[ValueType]
+ /** Is the is value type or type lambda? */
+ final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType]
+
/** Does this type denote a stable reference (i.e. singleton type)? */
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable
@@ -237,9 +239,14 @@ object Types {
/** The parts of this type which are type or term refs and which
* satisfy predicate `p`.
+ *
+ * @param p The predicate to satisfy
+ * @param excludeLowerBounds If set to true, the lower bounds of abstract
+ * types will be ignored.
*/
- def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] =
- new NamedPartsAccumulator(p).apply(mutable.LinkedHashSet(), this)
+ def namedPartsWith(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
+ (implicit ctx: Context): collection.Set[NamedType] =
+ new NamedPartsAccumulator(p, excludeLowerBounds).apply(mutable.LinkedHashSet(), this)
/** Map function `f` over elements of an AndType, rebuilding with function `g` */
def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match {
@@ -436,8 +443,12 @@ object Types {
tp.cls.findMember(name, pre, excluded)
case AndType(l, r) =>
goAnd(l, r)
- case OrType(l, r) =>
- goOr(l, r)
+ case tp: OrType =>
+ // we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
+ // achieved that by narrowing `pre` to each alternative, but it led to merge errors in
+ // lots of places. The present strategy is instead of widen `tp` using `join` to be a
+ // supertype of `pre`.
+ go(tp.join)
case tp: JavaArrayType =>
defn.ObjectType.findMember(name, pre, excluded)
case ErrorType =>
@@ -515,7 +526,7 @@ object Types {
}
def goApply(tp: HKApply) = tp.tycon match {
- case tl: TypeLambda =>
+ case tl: PolyType =>
go(tl.resType).mapInfo(info =>
tl.derivedLambdaAbstraction(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args))
case _ =>
@@ -556,7 +567,6 @@ object Types {
def goAnd(l: Type, r: Type) = {
go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name))
}
- def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
{ val recCount = ctx.findMemberCount + 1
ctx.findMemberCount = recCount
@@ -610,13 +620,13 @@ object Types {
/** The set of abstract term members of this type. */
final def abstractTermMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTermMembers") {
memberDenots(abstractTermNameFilter,
- (name, buf) => buf ++= member(name).altsWith(_ is Deferred))
+ (name, buf) => buf ++= nonPrivateMember(name).altsWith(_ is Deferred))
}
/** The set of abstract type members of this type. */
final def abstractTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTypeMembers") {
memberDenots(abstractTypeNameFilter,
- (name, buf) => buf += member(name).asSingleDenotation)
+ (name, buf) => buf += nonPrivateMember(name).asSingleDenotation)
}
/** The set of abstract type members of this type. */
@@ -1081,7 +1091,7 @@ object Types {
/** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */
final def firstParamTypes(implicit ctx: Context): List[Type] = this match {
case mt: MethodType => mt.paramTypes
- case pt: GenericType => pt.resultType.firstParamTypes
+ case pt: PolyType => pt.resultType.firstParamTypes
case _ => Nil
}
@@ -1230,28 +1240,6 @@ object Types {
*/
def simplified(implicit ctx: Context) = ctx.simplify(this, null)
- /** Approximations of union types: We replace a union type Tn | ... | Tn
- * by the smallest intersection type of baseclass instances of T1,...,Tn.
- * Example: Given
- *
- * trait C[+T]
- * trait D
- * class A extends C[A] with D
- * class B extends C[B] with D with E
- *
- * we approximate `A | B` by `C[A | B] with D`
- *
- * As a second measure we also homogenize refinements containing
- * type variables. For instance, if `A` is an instantiatable type variable,
- * then
- *
- * ArrayBuffer[Int] | ArrayBuffer[A]
- *
- * is approximated by instantiating `A` to `Int` and returning `ArrayBuffer[Int]`
- * instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
- */
- def approximateUnion(implicit ctx: Context) = ctx.approximateUnion(this)
-
/** customized hash code of this type.
* NotCached for uncached types. Cached types
* compute hash and use it as the type's hashCode.
@@ -2216,7 +2204,7 @@ object Types {
object AndType {
def apply(tp1: Type, tp2: Type)(implicit ctx: Context) = {
- assert(tp1.isInstanceOf[ValueType] && tp2.isInstanceOf[ValueType], i"$tp1 & $tp2 / " + s"$tp1 & $tp2")
+ assert(tp1.isValueType && tp2.isValueType, i"$tp1 & $tp2 / " + s"$tp1 & $tp2")
unchecked(tp1, tp2)
}
def unchecked(tp1: Type, tp2: Type)(implicit ctx: Context) = {
@@ -2233,9 +2221,24 @@ object Types {
}
abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {
+
assert(tp1.isInstanceOf[ValueType] && tp2.isInstanceOf[ValueType])
def isAnd = false
+ private[this] var myJoin: Type = _
+ private[this] var myJoinPeriod: Period = Nowhere
+
+ /** Replace or type by the closest non-or type above it */
+ def join(implicit ctx: Context): Type = {
+ if (myJoinPeriod != ctx.period) {
+ myJoin = ctx.orDominator(this)
+ core.println(i"join of $this == $myJoin")
+ assert(myJoin != this)
+ myJoinPeriod = ctx.period
+ }
+ myJoin
+ }
+
def derivedOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
else OrType.make(tp1, tp2)
@@ -2264,7 +2267,7 @@ object Types {
// and therefore two different poly types would never be equal.
/** A trait that mixes in functionality for signature caching */
- trait MethodicType extends Type {
+ trait MethodicType extends TermType {
private[this] var mySignature: Signature = _
private[this] var mySignatureRunId: Int = NoRunId
@@ -2504,82 +2507,67 @@ object Types {
}
}
- /** A common supertrait of PolyType and TypeLambda */
- trait GenericType extends BindingType with TermType {
-
- /** The names of the type parameters */
- val paramNames: List[TypeName]
+ /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */
+ class PolyType(val paramNames: List[TypeName], val variances: List[Int])(
+ paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type)
+ extends CachedProxyType with BindingType with MethodOrPoly {
/** The bounds of the type parameters */
- val paramBounds: List[TypeBounds]
+ val paramBounds: List[TypeBounds] = paramBoundsExp(this)
/** The result type of a PolyType / body of a type lambda */
- val resType: Type
+ val resType: Type = resultTypeExp(this)
- /** If this is a type lambda, the variances of its parameters, otherwise Nil.*/
- def variances: List[Int]
+ assert(resType.isInstanceOf[TermType], this)
+ assert(paramNames.nonEmpty)
- override def resultType(implicit ctx: Context) = resType
+ protected def computeSignature(implicit ctx: Context) = resultSignature
- /** Unconditionally create a new generic type like this one with given elements */
- def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context): GenericType
+ def isPolymorphicMethodType: Boolean = resType match {
+ case _: MethodType => true
+ case _ => false
+ }
+
+ /** PolyParam references to all type parameters of this type */
+ lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _))
+
+ lazy val typeParams: List[LambdaParam] =
+ paramNames.indices.toList.map(new LambdaParam(this, _))
+
+ override def resultType(implicit ctx: Context) = resType
+ override def underlying(implicit ctx: Context) = resType
/** Instantiate result type by substituting parameters with given arguments */
final def instantiate(argTypes: List[Type])(implicit ctx: Context): Type =
resultType.substParams(this, argTypes)
/** Instantiate parameter bounds by substituting parameters with given arguments */
- def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] =
+ final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] =
paramBounds.mapConserve(_.substParams(this, argTypes).bounds)
- def derivedGenericType(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context) =
- if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this
- else duplicate(paramNames, paramBounds, resType)
+ def newLikeThis(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType =
+ PolyType.apply(paramNames, variances)(
+ x => paramBounds mapConserve (_.subst(this, x).bounds),
+ x => resType.subst(this, x))
- /** PolyParam references to all type parameters of this type */
- lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _))
+ def derivedPolyType(paramNames: List[TypeName] = this.paramNames,
+ paramBounds: List[TypeBounds] = this.paramBounds,
+ resType: Type = this.resType)(implicit ctx: Context) =
+ if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this
+ else newLikeThis(paramNames, paramBounds, resType)
- /** The type `[tparams := paramRefs] tp`, where `tparams` can be
- * either a list of type parameter symbols or a list of lambda parameters
- */
- def lifted(tparams: List[TypeParamInfo], tp: Type)(implicit ctx: Context): Type =
- tparams match {
- case LambdaParam(poly, _) :: _ => tp.subst(poly, this)
- case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs)
+ def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type =
+ resType match {
+ case resType @ TypeAlias(alias) =>
+ resType.derivedTypeAlias(newLikeThis(paramNames, paramBounds, alias))
+ case resType @ TypeBounds(lo, hi) =>
+ resType.derivedTypeBounds(
+ if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramBounds, lo),
+ newLikeThis(paramNames, paramBounds, hi))
+ case _ =>
+ derivedPolyType(paramNames, paramBounds, resType)
}
- override def equals(other: Any) = other match {
- case other: GenericType =>
- other.paramNames == this.paramNames &&
- other.paramBounds == this.paramBounds &&
- other.resType == this.resType &&
- other.variances == this.variances
- case _ => false
- }
- }
-
- /** A type for polymorphic methods */
- class PolyType(val paramNames: List[TypeName])(paramBoundsExp: GenericType => List[TypeBounds], resultTypeExp: GenericType => Type)
- extends CachedGroundType with GenericType with MethodOrPoly {
- val paramBounds: List[TypeBounds] = paramBoundsExp(this)
- val resType: Type = resultTypeExp(this)
- def variances = Nil
-
- protected def computeSignature(implicit ctx: Context) = resultSignature
-
- def isPolymorphicMethodType: Boolean = resType match {
- case _: MethodType => true
- case _ => false
- }
-
- def derivedPolyType(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType =
- derivedGenericType(paramNames, paramBounds, resType).asInstanceOf[PolyType]
-
- def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context): PolyType =
- PolyType(paramNames)(
- x => paramBounds mapConserve (_.subst(this, x).bounds),
- x => resType.subst(this, x))
-
/** Merge nested polytypes into one polytype. nested polytypes are normally not supported
* but can arise as temporary data structures.
*/
@@ -2598,66 +2586,55 @@ object Types {
case _ => this
}
- override def toString = s"PolyType($paramNames, $paramBounds, $resType)"
+ /** The type `[tparams := paramRefs] tp`, where `tparams` can be
+ * either a list of type parameter symbols or a list of lambda parameters
+ */
+ def lifted(tparams: List[TypeParamInfo], tp: Type)(implicit ctx: Context): Type =
+ tparams match {
+ case LambdaParam(poly, _) :: _ => tp.subst(poly, this)
+ case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs)
+ }
+
+ override def equals(other: Any) = other match {
+ case other: PolyType =>
+ other.paramNames == this.paramNames &&
+ other.paramBounds == this.paramBounds &&
+ other.resType == this.resType &&
+ other.variances == this.variances
+ case _ => false
+ }
+
+ override def toString = s"PolyType($variances, $paramNames, $paramBounds, $resType)"
- override def computeHash = doHash(paramNames, resType, paramBounds)
+ override def computeHash = doHash(variances ::: paramNames, resType, paramBounds)
}
object PolyType {
- def apply(paramNames: List[TypeName])(paramBoundsExp: GenericType => List[TypeBounds], resultTypeExp: GenericType => Type)(implicit ctx: Context): PolyType = {
- unique(new PolyType(paramNames)(paramBoundsExp, resultTypeExp))
+ def apply(paramNames: List[TypeName], variances: List[Int] = Nil)(
+ paramBoundsExp: PolyType => List[TypeBounds],
+ resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = {
+ val vs = if (variances.isEmpty) paramNames.map(alwaysZero) else variances
+ unique(new PolyType(paramNames, vs)(paramBoundsExp, resultTypeExp))
}
- def fromSymbols(tparams: List[Symbol], resultType: Type)(implicit ctx: Context) =
+ def fromSymbols(tparams: List[Symbol], resultType: Type)(implicit ctx: Context): Type =
if (tparams.isEmpty) resultType
- else apply(tparams map (_.name.asTypeName))(
+ else apply(tparams map (_.name.asTypeName), tparams.map(_.variance))(
pt => tparams.map(tparam => pt.lifted(tparams, tparam.info).bounds),
pt => pt.lifted(tparams, resultType))
- }
-
- // ----- HK types: TypeLambda, LambdaParam, HKApply ---------------------
-
- /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */
- class TypeLambda(val paramNames: List[TypeName], val variances: List[Int])(paramBoundsExp: GenericType => List[TypeBounds], resultTypeExp: GenericType => Type)
- extends CachedProxyType with GenericType with ValueType {
- val paramBounds = paramBoundsExp(this)
- val resType = resultTypeExp(this)
-
- assert(resType.isInstanceOf[TermType], this)
- assert(paramNames.nonEmpty)
-
- override def underlying(implicit ctx: Context) = resType
- lazy val typeParams: List[LambdaParam] =
- paramNames.indices.toList.map(new LambdaParam(this, _))
-
- def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type =
- resType match {
- case resType @ TypeAlias(alias) =>
- resType.derivedTypeAlias(duplicate(paramNames, paramBounds, alias))
- case resType @ TypeBounds(lo, hi) =>
- resType.derivedTypeBounds(
- if (lo.isRef(defn.NothingClass)) lo else duplicate(paramNames, paramBounds, lo),
- duplicate(paramNames, paramBounds, hi))
- case _ =>
- derivedTypeLambda(paramNames, paramBounds, resType)
- }
-
- def derivedTypeLambda(paramNames: List[TypeName] = paramNames, paramBounds: List[TypeBounds] = paramBounds, resType: Type)(implicit ctx: Context): TypeLambda =
- derivedGenericType(paramNames, paramBounds, resType).asInstanceOf[TypeLambda]
-
- def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context): TypeLambda =
- TypeLambda(paramNames, variances)(
- x => paramBounds mapConserve (_.subst(this, x).bounds),
- x => resType.subst(this, x))
-
- override def toString = s"TypeLambda($variances, $paramNames, $paramBounds, $resType)"
+ def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] =
+ Some((tl.typeParams, tl.resType))
- override def computeHash = doHash(variances ::: paramNames, resType, paramBounds)
+ def any(n: Int)(implicit ctx: Context) =
+ apply(tpnme.syntheticLambdaParamNames(n), List.fill(n)(0))(
+ pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType)
}
+ // ----- HK types: LambdaParam, HKApply ---------------------
+
/** The parameter of a type lambda */
- case class LambdaParam(tl: TypeLambda, n: Int) extends TypeParamInfo {
+ case class LambdaParam(tl: PolyType, n: Int) extends TypeParamInfo {
def isTypeParam(implicit ctx: Context) = true
def paramName(implicit ctx: Context): TypeName = tl.paramNames(n)
def paramBounds(implicit ctx: Context): TypeBounds = tl.paramBounds(n)
@@ -2668,24 +2645,6 @@ object Types {
def paramRef(implicit ctx: Context): Type = PolyParam(tl, n)
}
- object TypeLambda {
- def apply(paramNames: List[TypeName], variances: List[Int])(paramBoundsExp: GenericType => List[TypeBounds], resultTypeExp: GenericType => Type)(implicit ctx: Context): TypeLambda = {
- unique(new TypeLambda(paramNames, variances)(paramBoundsExp, resultTypeExp))
- }
-
- def fromSymbols(tparams: List[Symbol], resultType: Type)(implicit ctx: Context) =
- if (tparams.isEmpty) resultType
- else apply(tparams map (_.name.asTypeName), tparams.map(_.variance))(
- pt => tparams.map(tparam => pt.lifted(tparams, tparam.info).bounds),
- pt => pt.lifted(tparams, resultType))
- def unapply(tl: TypeLambda): Some[(List[LambdaParam], Type)] =
- Some((tl.typeParams, tl.resType))
-
- def any(n: Int)(implicit ctx: Context) =
- apply(tpnme.syntheticLambdaParamNames(n), List.fill(n)(0))(
- pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType)
- }
-
/** A higher kinded type application `C[T_1, ..., T_n]` */
abstract case class HKApply(tycon: Type, args: List[Type])
extends CachedProxyType with ValueType {
@@ -2698,7 +2657,7 @@ object Types {
override def superType(implicit ctx: Context): Type = {
if (ctx.period != validSuper) {
cachedSuper = tycon match {
- case tp: TypeLambda => defn.AnyType
+ case tp: PolyType => defn.AnyType
case tp: TypeVar if !tp.inst.exists =>
// supertype not stable, since underlying might change
return tp.underlying.applyIfParameterized(args)
@@ -2724,7 +2683,7 @@ object Types {
def typeParams(implicit ctx: Context): List[TypeParamInfo] = {
val tparams = tycon.typeParams
- if (tparams.isEmpty) TypeLambda.any(args.length).typeParams else tparams
+ if (tparams.isEmpty) PolyType.any(args.length).typeParams else tparams
}
def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type =
@@ -2737,7 +2696,7 @@ object Types {
def check(tycon: Type): Unit = tycon.stripTypeVar match {
case tycon: TypeRef if !tycon.symbol.isClass =>
case _: PolyParam | ErrorType | _: WildcardType =>
- case _: TypeLambda =>
+ case _: PolyType =>
assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this")
case tycon: AnnotatedType =>
check(tycon.underlying)
@@ -2803,8 +2762,8 @@ object Types {
}
/** TODO Some docs would be nice here! */
- case class PolyParam(binder: GenericType, paramNum: Int) extends ParamType {
- type BT = GenericType
+ case class PolyParam(binder: PolyType, paramNum: Int) extends ParamType {
+ type BT = PolyType
def copyBoundType(bt: BT) = PolyParam(bt, paramNum)
/** Looking only at the structure of `bound`, is one of the following true?
@@ -3410,8 +3369,8 @@ object Types {
tp.derivedMethodType(tp.paramNames, formals, restpe)
protected def derivedExprType(tp: ExprType, restpe: Type): Type =
tp.derivedExprType(restpe)
- protected def derivedGenericType(tp: GenericType, pbounds: List[TypeBounds], restpe: Type): Type =
- tp.derivedGenericType(tp.paramNames, pbounds, restpe)
+ protected def derivedPolyType(tp: PolyType, pbounds: List[TypeBounds], restpe: Type): Type =
+ tp.derivedPolyType(tp.paramNames, pbounds, restpe)
/** Map this function over given type */
def mapOver(tp: Type): Type = {
@@ -3453,12 +3412,12 @@ object Types {
case tp: ExprType =>
derivedExprType(tp, this(tp.resultType))
- case tp: GenericType =>
+ case tp: PolyType =>
def mapOverPoly = {
variance = -variance
val bounds1 = tp.paramBounds.mapConserve(this).asInstanceOf[List[TypeBounds]]
variance = -variance
- derivedGenericType(tp, bounds1, this(tp.resultType))
+ derivedPolyType(tp, bounds1, this(tp.resultType))
}
mapOverPoly
@@ -3671,7 +3630,7 @@ object Types {
case ExprType(restpe) =>
this(x, restpe)
- case tp: GenericType =>
+ case tp: PolyType =>
variance = -variance
val y = foldOver(x, tp.paramBounds)
variance = -variance
@@ -3756,7 +3715,8 @@ object Types {
def apply(x: Boolean, tp: Type) = x || tp.isUnsafeNonvariant || foldOver(x, tp)
}
- class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
+ class NamedPartsAccumulator(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
+ (implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
override def stopAtStatic = false
def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x
val seen: mutable.Set[Type] = mutable.Set()
@@ -3769,7 +3729,8 @@ object Types {
apply(foldOver(maybeAdd(x, tp), tp), tp.underlying)
case tp: TypeRef =>
foldOver(maybeAdd(x, tp), tp)
- case TypeBounds(_, hi) =>
+ case TypeBounds(lo, hi) =>
+ if (!excludeLowerBounds) apply(x, lo)
apply(x, hi)
case tp: ThisType =>
apply(x, tp.tref)
@@ -3802,7 +3763,7 @@ object Types {
object abstractTypeNameFilter extends NameFilter {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
name.isTypeName && {
- val mbr = pre.member(name)
+ val mbr = pre.nonPrivateMember(name)
(mbr.symbol is Deferred) && mbr.info.isInstanceOf[RealTypeBounds]
}
}
@@ -3819,7 +3780,7 @@ object Types {
/** A filter for names of deferred term definitions of a given type */
object abstractTermNameFilter extends NameFilter {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
- name.isTermName && (pre member name).hasAltWith(_.symbol is Deferred)
+ name.isTermName && pre.nonPrivateMember(name).hasAltWith(_.symbol is Deferred)
}
object typeNameFilter extends NameFilter {
diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index b6f52c0ec..8889e8a5c 100644
--- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -251,7 +251,7 @@ class TreePickler(pickler: TastyPickler) {
case tpe: ExprType =>
writeByte(BYNAMEtype)
pickleType(tpe.underlying)
- case tpe: TypeLambda =>
+ case tpe: PolyType =>
writeByte(LAMBDAtype)
val paramNames = tpe.typeParams.map(tparam =>
varianceToPrefix(tparam.paramVariance) +: tparam.paramName)
@@ -259,9 +259,6 @@ class TreePickler(pickler: TastyPickler) {
case tpe: MethodType if richTypes =>
writeByte(METHODtype)
pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes)
- case tpe: PolyType if richTypes =>
- writeByte(POLYtype)
- pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramBounds)
case tpe: PolyParam =>
if (!pickleParamType(tpe))
// TODO figure out why this case arises in e.g. pickling AbstractFileReader.
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index 09f2c0d1f..f67159808 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -270,7 +270,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
val (rawNames, paramReader) = readNamesSkipParams
val (variances, paramNames) = rawNames
.map(name => (prefixToVariance(name.head), name.tail.toTypeName)).unzip
- val result = TypeLambda(paramNames, variances)(
+ val result = PolyType(paramNames, variances)(
pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)),
pt => readType())
goto(end)
@@ -290,7 +290,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
result
case PARAMtype =>
readTypeRef() match {
- case binder: GenericType => PolyParam(binder, readNat())
+ case binder: PolyType => PolyParam(binder, readNat())
case binder: MethodType => MethodParam(binder, readNat())
}
case CLASSconst =>
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 9aadf0c61..46e7512f3 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -27,6 +27,8 @@ import rewrite.Rewrites.patch
object Parsers {
import ast.untpd._
+ import reporting.diagnostic.Message
+ import reporting.diagnostic.messages._
case class OpInfo(operand: Tree, operator: Name, offset: Offset)
@@ -97,7 +99,7 @@ object Parsers {
/** Issue an error at given offset if beyond last error offset
* and update lastErrorOffset.
*/
- def syntaxError(msg: String, offset: Int = in.offset): Unit =
+ def syntaxError(msg: Message, offset: Int = in.offset): Unit =
if (offset > lastErrorOffset) {
syntaxError(msg, Position(offset))
lastErrorOffset = in.offset
@@ -106,7 +108,7 @@ object Parsers {
/** Unconditionally issue an error at given position, without
* updating lastErrorOffset.
*/
- def syntaxError(msg: String, pos: Position): Unit =
+ def syntaxError(msg: Message, pos: Position): Unit =
ctx.error(msg, source atPos pos)
}
@@ -213,20 +215,23 @@ object Parsers {
}
}
- def warning(msg: String, offset: Int = in.offset) =
+ def warning(msg: Message, sourcePos: SourcePosition) =
+ ctx.warning(msg, sourcePos)
+
+ def warning(msg: Message, offset: Int = in.offset) =
ctx.warning(msg, source atPos Position(offset))
- def deprecationWarning(msg: String, offset: Int = in.offset) =
+ def deprecationWarning(msg: Message, offset: Int = in.offset) =
ctx.deprecationWarning(msg, source atPos Position(offset))
/** Issue an error at current offset taht input is incomplete */
- def incompleteInputError(msg: String) =
+ def incompleteInputError(msg: Message) =
ctx.incompleteInputError(msg, source atPos Position(in.offset))
/** If at end of file, issue an incompleteInputError.
* Otherwise issue a syntax error and skip to next safe point.
*/
- def syntaxErrorOrIncomplete(msg: String) =
+ def syntaxErrorOrIncomplete(msg: Message) =
if (in.token == EOF) incompleteInputError(msg)
else {
syntaxError(msg)
@@ -696,9 +701,9 @@ object Parsers {
else if (in.token == LBRACKET) {
val start = in.offset
val tparams = typeParamClause(ParamOwner.TypeParam)
- if (isIdent && in.name.toString == "->")
- atPos(start, in.skipToken())(TypeLambdaTree(tparams, typ()))
- else { syntaxErrorOrIncomplete(expectedMessage("`->'")); typ() }
+ if (in.token == ARROW)
+ atPos(start, in.skipToken())(PolyTypeTree(tparams, typ()))
+ else { accept(ARROW); typ() }
}
else infixType()
@@ -732,7 +737,7 @@ object Parsers {
def withTypeRest(t: Tree): Tree =
if (in.token == WITH) {
- deprecationWarning("`with' as a type operator has been deprecated; use `&' instead")
+ deprecationWarning(DeprecatedWithOperator())
in.nextToken()
AndTypeTree(t, withType())
}
@@ -1004,28 +1009,33 @@ object Parsers {
DoWhile(body, cond)
}
case TRY =>
+ val tryOffset = in.offset
atPos(in.skipToken()) {
val body = expr()
- val handler =
+ val (handler, handlerStart) =
if (in.token == CATCH) {
+ val pos = in.offset
in.nextToken()
- expr()
- } else EmptyTree
+ (expr(), pos)
+ } else (EmptyTree, -1)
handler match {
- case Block(Nil, EmptyTree) => syntaxError(
- "`catch` block does not contain a valid expression, try adding a case like - `case e: Exception =>` to the block",
- handler.pos
- )
+ case Block(Nil, EmptyTree) =>
+ assert(handlerStart != -1)
+ syntaxError(
+ new EmptyCatchBlock(body),
+ Position(handlerStart, handler.pos.end)
+ )
case _ =>
}
val finalizer =
if (in.token == FINALLY) { accept(FINALLY); expr() }
else {
- if (handler.isEmpty)
- warning("A try without `catch` or `finally` is equivalent to putting its body in a block; no exceptions are handled.")
-
+ if (handler.isEmpty) warning(
+ EmptyCatchAndFinallyBlock(body),
+ source atPos Position(tryOffset, body.pos.end)
+ )
EmptyTree
}
ParsedTry(body, handler, finalizer)
@@ -2051,7 +2061,7 @@ object Parsers {
def templateBody(): (ValDef, List[Tree]) = {
val r = inDefScopeBraces { templateStatSeq() }
if (in.token == WITH) {
- syntaxError("early definitions are not supported; use trait parameters instead")
+ syntaxError(EarlyDefinitionsNotSupported())
in.nextToken()
template(emptyConstructor)
}
diff --git a/src/dotty/tools/dotc/printing/Formatting.scala b/src/dotty/tools/dotc/printing/Formatting.scala
index 8921c56a3..e7968b14a 100644
--- a/src/dotty/tools/dotc/printing/Formatting.scala
+++ b/src/dotty/tools/dotc/printing/Formatting.scala
@@ -8,7 +8,10 @@ import collection.Map
import Decorators._
import scala.annotation.switch
import scala.util.control.NonFatal
-import reporting.Diagnostic
+import reporting.diagnostic.MessageContainer
+import util.DiffUtil
+import Highlighting._
+import SyntaxHighlighting._
object Formatting {
@@ -66,17 +69,39 @@ object Formatting {
* message composition methods, this is crucial.
*/
class ErrorMessageFormatter(sc: StringContext) extends StringFormatter(sc) {
- override protected def showArg(arg: Any)(implicit ctx: Context): String = {
- def isSensical(arg: Any): Boolean = arg match {
- case tpe: Type =>
- tpe.exists && !tpe.isErroneous
- case sym: Symbol if sym.isCompleted =>
- sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info.exists
- case _ => true
+ override protected def showArg(arg: Any)(implicit ctx: Context): String =
+ wrapNonSensical(arg, super.showArg(arg))
+ }
+
+ class SyntaxFormatter(sc: StringContext) extends StringFormatter(sc) {
+ override protected def showArg(arg: Any)(implicit ctx: Context): String =
+ arg match {
+ case arg: Showable if ctx.settings.color.value != "never" =>
+ val highlighted =
+ SyntaxHighlighting(wrapNonSensical(arg, super.showArg(arg)))
+ new String(highlighted.toArray)
+ case hl: Highlight =>
+ hl.show
+ case hb: HighlightBuffer =>
+ hb.toString
+ case str: String if ctx.settings.color.value != "never" =>
+ new String(SyntaxHighlighting(str).toArray)
+ case _ => super.showArg(arg)
}
- val str = super.showArg(arg)
- if (isSensical(arg)) str else Diagnostic.nonSensicalStartTag + str + Diagnostic.nonSensicalEndTag
+ }
+
+ private def wrapNonSensical(arg: Any /* Type | Symbol */, str: String)(implicit ctx: Context): String = {
+ import MessageContainer._
+ def isSensical(arg: Any): Boolean = arg match {
+ case tpe: Type =>
+ tpe.exists && !tpe.isErroneous
+ case sym: Symbol if sym.isCompleted =>
+ sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info.exists
+ case _ => true
}
+
+ if (isSensical(arg)) str
+ else nonSensicalStartTag + str + nonSensicalEndTag
}
private type Recorded = AnyRef /*Symbol | PolyParam*/
@@ -111,65 +136,123 @@ object Formatting {
seen.record(super.polyParamNameString(param), param)
}
- def explained2(op: Context => String)(implicit ctx: Context): String = {
- val seen = new Seen
- val explainCtx = ctx.printer match {
- case dp: ExplainingPrinter => ctx // re-use outer printer and defer explanation to it
- case _ => ctx.fresh.setPrinterFn(ctx => new ExplainingPrinter(seen)(ctx))
- }
+ /** Create explanation for single `Recorded` type or symbol */
+ def explanation(entry: AnyRef)(implicit ctx: Context): String = {
+ def boundStr(bound: Type, default: ClassSymbol, cmp: String) =
+ if (bound.isRef(default)) "" else i"$cmp $bound"
- def explanation(entry: Recorded): String = {
- def boundStr(bound: Type, default: ClassSymbol, cmp: String) =
- if (bound.isRef(default)) "" else i"$cmp $bound"
+ def boundsStr(bounds: TypeBounds): String = {
+ val lo = boundStr(bounds.lo, defn.NothingClass, ">:")
+ val hi = boundStr(bounds.hi, defn.AnyClass, "<:")
+ if (lo.isEmpty) hi
+ else if (hi.isEmpty) lo
+ else s"$lo and $hi"
+ }
- def boundsStr(bounds: TypeBounds): String = {
- val lo = boundStr(bounds.lo, defn.NothingClass, ">:")
- val hi = boundStr(bounds.hi, defn.AnyClass, "<:")
- if (lo.isEmpty) hi
- else if (hi.isEmpty) lo
- else s"$lo and $hi"
- }
+ def addendum(cat: String, info: Type): String = info match {
+ case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty =>
+ if (lo eq hi) i" which is an alias of $lo"
+ else i" with $cat ${boundsStr(bounds)}"
+ case _ =>
+ ""
+ }
- def addendum(cat: String, info: Type)(implicit ctx: Context): String = info match {
- case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty =>
- if (lo eq hi) i" which is an alias of $lo"
- else i" with $cat ${boundsStr(bounds)}"
- case _ =>
- ""
- }
+ entry match {
+ case param: PolyParam =>
+ s"is a type variable${addendum("constraint", ctx.typeComparer.bounds(param))}"
+ case sym: Symbol =>
+ s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", sym.info)}"
+ }
+ }
- entry match {
- case param: PolyParam =>
- s"is a type variable${addendum("constraint", ctx.typeComparer.bounds(param))}"
- case sym: Symbol =>
- s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", sym.info)}"
- }
+ /** Turns a `Seen` into a `String` to produce an explanation for types on the
+ * form `where: T is...`
+ *
+ * @return string disambiguating types
+ */
+ private def explanations(seen: Seen)(implicit ctx: Context): String = {
+ def needsExplanation(entry: Recorded) = entry match {
+ case param: PolyParam => ctx.typerState.constraint.contains(param)
+ case _ => false
}
- def explanations(seen: Seen)(implicit ctx: Context): String = {
- def needsExplanation(entry: Recorded) = entry match {
- case param: PolyParam => ctx.typerState.constraint.contains(param)
- case _ => false
+ val toExplain: List[(String, Recorded)] = seen.toList.flatMap {
+ case (str, entry :: Nil) =>
+ if (needsExplanation(entry)) (str, entry) :: Nil else Nil
+ case (str, entries) =>
+ entries.map(alt => (seen.record(str, alt), alt))
+ }.sortBy(_._1)
+
+ def columnar(parts: List[(String, String)]): List[String] = {
+ lazy val maxLen = parts.map(_._1.length).max
+ parts.map {
+ case (leader, trailer) =>
+ val variable = hl"$leader"
+ s"""$variable${" " * (maxLen - leader.length)} $trailer"""
}
- val toExplain: List[(String, Recorded)] = seen.toList.flatMap {
- case (str, entry :: Nil) =>
- if (needsExplanation(entry)) (str, entry) :: Nil else Nil
- case (str, entries) =>
- entries.map(alt => (seen.record(str, alt), alt))
- }.sortBy(_._1)
- val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) }
- val explainLines = columnar(explainParts, " ")
- if (explainLines.isEmpty) "" else i"\n\nwhere $explainLines%\n %\n"
}
- op(explainCtx) ++ explanations(seen)
+ val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) }
+ val explainLines = columnar(explainParts)
+ if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n"
}
- def columnar(parts: List[(String, String)], sep: String): List[String] = {
- lazy val maxLen = parts.map(_._1.length).max
- parts.map {
- case (leader, trailer) =>
- s"$leader${" " * (maxLen - leader.length)}$sep$trailer"
+ /** Context with correct printer set for explanations */
+ private def explainCtx(seen: Seen)(implicit ctx: Context): Context = ctx.printer match {
+ case dp: ExplainingPrinter =>
+ ctx // re-use outer printer and defer explanation to it
+ case _ => ctx.fresh.setPrinterFn(ctx => new ExplainingPrinter(seen)(ctx))
+ }
+
+ /** Entrypoint for explanation string interpolator:
+ *
+ * ```
+ * ex"disambiguate $tpe1 and $tpe2"
+ * ```
+ */
+ def explained2(op: Context => String)(implicit ctx: Context): String = {
+ val seen = new Seen
+ op(explainCtx(seen)) ++ explanations(seen)
+ }
+
+ /** When getting a type mismatch it is useful to disambiguate placeholders like:
+ *
+ * ```
+ * found: List[Int]
+ * required: List[T]
+ * where: T is a type in the initalizer of value s which is an alias of
+ * String
+ * ```
+ *
+ * @return the `where` section as well as the printing context for the
+ * placeholders - `("T is a...", printCtx)`
+ */
+ def disambiguateTypes(args: Type*)(implicit ctx: Context): (String, Context) = {
+ val seen = new Seen
+ val printCtx = explainCtx(seen)
+ args.foreach(_.show(printCtx)) // showing each member will put it into `seen`
+ (explanations(seen), printCtx)
+ }
+
+ /** This method will produce a colored type diff from the given arguments.
+ * The idea is to do this for known cases that are useful and then fall back
+ * on regular syntax highlighting for the cases which are unhandled.
+ *
+ * Please not that if used in combination with `disambiguateTypes` the
+ * correct `Context` for printing should also be passed when calling the
+ * method.
+ *
+ * @return the (found, expected, changePercentage) with coloring to
+ * highlight the difference
+ */
+ def typeDiff(found: Type, expected: Type)(implicit ctx: Context): (String, String) = {
+ val fnd = wrapNonSensical(found, found.show)
+ val exp = wrapNonSensical(expected, expected.show)
+
+ DiffUtil.mkColoredTypeDiff(fnd, exp) match {
+ case _ if ctx.settings.color.value == "never" => (fnd, exp)
+ case (fnd, exp, change) if change < 0.5 => (fnd, exp)
+ case _ => (fnd, exp)
}
}
}
diff --git a/src/dotty/tools/dotc/printing/Highlighting.scala b/src/dotty/tools/dotc/printing/Highlighting.scala
new file mode 100644
index 000000000..3bda7fb7a
--- /dev/null
+++ b/src/dotty/tools/dotc/printing/Highlighting.scala
@@ -0,0 +1,77 @@
+package dotty.tools
+package dotc
+package printing
+
+import scala.collection.mutable
+import core.Contexts.Context
+
+object Highlighting {
+
+ implicit def highlightShow(h: Highlight)(implicit ctx: Context): String =
+ h.show
+
+ abstract class Highlight(private val highlight: String) {
+ def text: String
+
+ def show(implicit ctx: Context) =
+ if (ctx.settings.color.value == "never") text
+ else highlight + text + Console.RESET
+
+ override def toString =
+ highlight + text + Console.RESET
+
+ def +(other: Highlight)(implicit ctx: Context): HighlightBuffer =
+ new HighlightBuffer(this) + other
+
+ def +(other: String)(implicit ctx: Context): HighlightBuffer =
+ new HighlightBuffer(this) + other
+ }
+
+ abstract class Modifier(private val mod: String, text: String) extends Highlight(Console.RESET) {
+ override def show(implicit ctx: Context) =
+ if (ctx.settings.color.value == "never") ""
+ else mod + super.show
+ }
+
+ case class HighlightBuffer(hl: Highlight)(implicit ctx: Context) {
+ val buffer = new mutable.ListBuffer[String]
+
+ buffer += hl.show
+
+ def +(other: Highlight): HighlightBuffer = {
+ buffer += other.show
+ this
+ }
+
+ def +(other: String): HighlightBuffer = {
+ buffer += other
+ this
+ }
+
+ override def toString =
+ buffer.mkString
+ }
+
+ case class NoColor(text: String) extends Highlight(Console.RESET)
+
+ case class Red(text: String) extends Highlight(Console.RED)
+ case class Blue(text: String) extends Highlight(Console.BLUE)
+ case class Cyan(text: String) extends Highlight(Console.CYAN)
+ case class Black(text: String) extends Highlight(Console.BLACK)
+ case class Green(text: String) extends Highlight(Console.GREEN)
+ case class White(text: String) extends Highlight(Console.WHITE)
+ case class Yellow(text: String) extends Highlight(Console.YELLOW)
+ case class Magenta(text: String) extends Highlight(Console.MAGENTA)
+
+ case class RedB(text: String) extends Highlight(Console.RED_B)
+ case class BlueB(text: String) extends Highlight(Console.BLUE_B)
+ case class CyanB(text: String) extends Highlight(Console.CYAN_B)
+ case class BlackB(text: String) extends Highlight(Console.BLACK_B)
+ case class GreenB(text: String) extends Highlight(Console.GREEN_B)
+ case class WhiteB(text: String) extends Highlight(Console.WHITE_B)
+ case class YellowB(text: String) extends Highlight(Console.YELLOW_B)
+ case class MagentaB(text: String) extends Highlight(Console.MAGENTA_B)
+
+ case class Bold(text: String) extends Modifier(Console.BOLD, text)
+ case class Underlined(text: String) extends Modifier(Console.UNDERLINED, text)
+}
diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala
index a92095d9b..785f57897 100644
--- a/src/dotty/tools/dotc/printing/PlainPrinter.scala
+++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala
@@ -114,25 +114,6 @@ class PlainPrinter(_ctx: Context) extends Printer {
case _ => toTextGlobal(arg)
}
- /** The text for a TypeLambda
- *
- * [v_1 p_1: B_1, ..., v_n p_n: B_n] -> T
- *
- * where
- * @param paramNames = p_1, ..., p_n
- * @param variances = v_1, ..., v_n
- * @param argBoundss = B_1, ..., B_n
- * @param body = T
- */
- protected def typeLambdaText(paramNames: List[String], variances: List[Int], argBoundss: List[TypeBounds], body: Type): Text = {
- def lambdaParamText(variance: Int, name: String, bounds: TypeBounds): Text =
- varianceString(variance) ~ name ~ toText(bounds)
- changePrec(GlobalPrec) {
- "[" ~ Text((variances, paramNames, argBoundss).zipped.map(lambdaParamText), ", ") ~
- "] -> " ~ toTextGlobal(body)
- }
- }
-
/** The longest sequence of refinement types, starting at given type
* and following parents.
*/
@@ -185,15 +166,12 @@ class PlainPrinter(_ctx: Context) extends Printer {
}
case tp: ExprType =>
changePrec(GlobalPrec) { "=> " ~ toText(tp.resultType) }
- case tp: TypeLambda =>
- typeLambdaText(tp.paramNames.map(_.toString), tp.variances, tp.paramBounds, tp.resultType)
case tp: PolyType =>
- def paramText(name: TypeName, bounds: TypeBounds): Text =
- polyParamNameString(name) ~ polyHash(tp) ~ toText(bounds)
+ def paramText(variance: Int, name: Name, bounds: TypeBounds): Text =
+ varianceString(variance) ~ name.toString ~ toText(bounds)
changePrec(GlobalPrec) {
- "[" ~
- Text((tp.paramNames, tp.paramBounds).zipped map paramText, ", ") ~
- "]" ~ toText(tp.resultType)
+ "[" ~ Text((tp.variances, tp.paramNames, tp.paramBounds).zipped.map(paramText), ", ") ~
+ "] => " ~ toTextGlobal(tp.resultType)
}
case tp: PolyParam =>
polyParamNameString(tp) ~ polyHash(tp.binder)
@@ -229,7 +207,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
protected def simpleNameString(sym: Symbol): String = nameString(sym.name)
/** If -uniqid is set, the hashcode of the polytype, after a # */
- protected def polyHash(pt: GenericType): Text =
+ protected def polyHash(pt: PolyType): Text =
if (ctx.settings.uniqid.value) "#" + pt.hashCode else ""
/** If -uniqid is set, the unique id of symbol, after a # */
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index 4f3a8d272..6315cfabc 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -20,7 +20,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
/** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */
private var enclosingDef: untpd.Tree = untpd.EmptyTree
- private var lambdaNestingLevel: Int = 0
private var myCtx: Context = _ctx
override protected[this] implicit def ctx: Context = myCtx
@@ -368,7 +367,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
toTextLocal(tpt) ~ " " ~ blockText(refines)
case AppliedTypeTree(tpt, args) =>
toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]"
- case TypeLambdaTree(tparams, body) =>
+ case PolyTypeTree(tparams, body) =>
changePrec(GlobalPrec) {
tparamsText(tparams) ~ " -> " ~ toText(body)
}
@@ -553,7 +552,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
(sym.allOverriddenSymbols exists (_ is TypeParam))
override def toText(sym: Symbol): Text = {
- if (sym.name == nme.IMPORT) {
+ if (sym.isImport) {
def importString(tree: untpd.Tree) = s"import ${tree.show}"
sym.infoOrCompleter match {
case info: Namer#Completer => return importString(info.original)
diff --git a/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala
index 83c428976..86f34e64d 100644
--- a/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala
+++ b/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala
@@ -5,21 +5,29 @@ package printing
import parsing.Tokens._
import scala.annotation.switch
import scala.collection.mutable.StringBuilder
+import core.Contexts.Context
+import Highlighting.{Highlight, HighlightBuffer}
/** This object provides functions for syntax highlighting in the REPL */
object SyntaxHighlighting {
+
val NoColor = Console.RESET
- val CommentColor = Console.GREEN
- val KeywordColor = Console.CYAN
- val LiteralColor = Console.MAGENTA
- val TypeColor = Console.GREEN
- val AnnotationColor = Console.RED
+ val CommentColor = Console.BLUE
+ val KeywordColor = Console.YELLOW
+ val ValDefColor = Console.CYAN
+ val LiteralColor = Console.RED
+ val TypeColor = Console.MAGENTA
+ val AnnotationColor = Console.MAGENTA
- private def none(str: String) = str
- private def keyword(str: String) = KeywordColor + str + NoColor
- private def typeDef(str: String) = TypeColor + str + NoColor
- private def literal(str: String) = LiteralColor + str + NoColor
- private def annotation(str: String) = AnnotationColor + str + NoColor
+ private def none(str: String) = str
+ private def keyword(str: String) = KeywordColor + str + NoColor
+ private def typeDef(str: String) = TypeColor + str + NoColor
+ private def literal(str: String) = LiteralColor + str + NoColor
+ private def valDef(str: String) = ValDefColor + str + NoColor
+ private def operator(str: String) = TypeColor + str + NoColor
+ private def annotation(str: String) =
+ if (str.trim == "@") str else AnnotationColor + str + NoColor
+ private val tripleQs = Console.RED_B + "???" + NoColor
private val keywords: Seq[String] = for {
index <- IF to INLINE // All alpha keywords
@@ -33,15 +41,18 @@ object SyntaxHighlighting {
'q' :: 'r' :: 's' :: 't' :: 'u' :: 'v' :: 'w' :: 'x' :: 'y' :: 'z' :: Nil
private val typeEnders =
- '{' :: '}' :: ')' :: '(' :: '=' :: ' ' :: ',' :: '.' :: '\n' :: Nil
+ '{' :: '}' :: ')' :: '(' :: '[' :: ']' :: '=' :: ' ' :: ',' :: '.' ::
+ '\n' :: Nil
- def apply(chars: Iterable[Char]): Vector[Char] = {
+ def apply(chars: Iterable[Char]): Iterable[Char] = {
var prev: Char = 0
var remaining = chars.toStream
val newBuf = new StringBuilder
+ var lastToken = ""
@inline def keywordStart =
- prev == 0 || prev == ' ' || prev == '{' || prev == '(' || prev == '\n'
+ prev == 0 || prev == ' ' || prev == '{' || prev == '(' ||
+ prev == '\n' || prev == '[' || prev == ','
@inline def numberStart(c: Char) =
c.isDigit && (!prev.isLetter || prev == '.' || prev == ' ' || prev == '(' || prev == '\u0000')
@@ -67,7 +78,9 @@ object SyntaxHighlighting {
if (n.isUpper && keywordStart) {
appendWhile(n, !typeEnders.contains(_), typeDef)
} else if (keywordStart) {
- append(n, keywords.contains(_), keyword)
+ append(n, keywords.contains(_), { kw =>
+ if (kw == "new") typeDef(kw) else keyword(kw)
+ })
} else {
newBuf += n
prev = n
@@ -89,17 +102,17 @@ object SyntaxHighlighting {
}
} else newBuf += '/'
case '=' =>
- append('=', _ == "=>", keyword)
+ append('=', _ == "=>", operator)
case '<' =>
- append('<', { x => x == "<-" || x == "<:" || x == "<%" }, keyword)
+ append('<', { x => x == "<-" || x == "<:" || x == "<%" }, operator)
case '>' =>
- append('>', { x => x == ">:" }, keyword)
+ append('>', { x => x == ">:" }, operator)
case '#' =>
- if (prev != ' ' && prev != '.') newBuf append keyword("#")
+ if (prev != ' ' && prev != '.') newBuf append operator("#")
else newBuf += n
prev = '#'
case '@' =>
- appendWhile('@', _ != ' ', annotation)
+ appendWhile('@', !typeEnders.contains(_), annotation)
case '\"' =>
appendLiteral('\"', multiline = remaining.take(2).mkString == "\"\"")
case '\'' =>
@@ -107,7 +120,11 @@ object SyntaxHighlighting {
case '`' =>
appendTo('`', _ == '`', none)
case _ => {
- if (n.isUpper && keywordStart)
+ if (n == '?' && remaining.take(2).mkString == "??") {
+ takeChars(2)
+ newBuf append tripleQs
+ prev = '?'
+ } else if (n.isUpper && keywordStart)
appendWhile(n, !typeEnders.contains(_), typeDef)
else if (numberStart(n))
appendWhile(n, { x => x.isDigit || x == '.' || x == '\u0000'}, literal)
@@ -169,7 +186,7 @@ object SyntaxHighlighting {
prev = '$'
} else if (next == '{') {
var open = 1 // keep track of open blocks
- newBuf append (KeywordColor + curr)
+ newBuf append (ValDefColor + curr)
newBuf += next
while (remaining.nonEmpty && open > 0) {
var c = takeChar()
@@ -179,7 +196,7 @@ object SyntaxHighlighting {
}
newBuf append LiteralColor
} else {
- newBuf append (KeywordColor + curr)
+ newBuf append (ValDefColor + curr)
newBuf += next
var c: Char = 'a'
while (c.isLetterOrDigit && remaining.nonEmpty) {
@@ -227,15 +244,32 @@ object SyntaxHighlighting {
def append(c: Char, shouldHL: String => Boolean, highlight: String => String) = {
var curr: Char = 0
val sb = new StringBuilder(s"$c")
- while (remaining.nonEmpty && curr != ' ' && curr != '(' && curr != '\n') {
+
+ def delim(c: Char) = (c: @switch) match {
+ case ' ' => true
+ case '\n' => true
+ case '(' => true
+ case '[' => true
+ case ':' => true
+ case '@' => true
+ case _ => false
+ }
+
+ while (remaining.nonEmpty && !delim(curr)) {
curr = takeChar()
- if (curr != ' ' && curr != '\n') sb += curr
+ if (!delim(curr)) sb += curr
}
val str = sb.toString
- val toAdd = if (shouldHL(str)) highlight(str) else str
- val suffix = if (curr == ' ' || curr == '\n') s"$curr" else ""
+ val toAdd =
+ if (shouldHL(str))
+ highlight(str)
+ else if (("var" :: "val" :: "def" :: "case" :: Nil).contains(lastToken))
+ valDef(str)
+ else str
+ val suffix = if (delim(curr)) s"$curr" else ""
newBuf append (toAdd + suffix)
+ lastToken = str
prev = curr
}
@@ -265,6 +299,6 @@ object SyntaxHighlighting {
prev = curr
}
- newBuf.toVector
+ newBuf.toIterable
}
}
diff --git a/src/dotty/tools/dotc/repl/AmmoniteReader.scala b/src/dotty/tools/dotc/repl/AmmoniteReader.scala
index 614654a28..f3b68e4b0 100644
--- a/src/dotty/tools/dotc/repl/AmmoniteReader.scala
+++ b/src/dotty/tools/dotc/repl/AmmoniteReader.scala
@@ -28,8 +28,8 @@ class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extend
val selectionFilter = GUILikeFilters.SelectionFilter(indent = 2)
val multilineFilter: Filter = Filter("multilineFilter") {
case TermState(lb ~: rest, b, c, _)
- if (lb == 10 || lb == 13) && incompleteInput(b.mkString) =>
- BasicFilters.injectNewLine(b, c, rest)
+ if (lb == 10 || lb == 13) && incompleteInput(b.mkString) =>
+ BasicFilters.injectNewLine(b, c, rest, indent = 2)
}
def readLine(prompt: String): String = {
@@ -61,7 +61,7 @@ class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extend
if (ctx.useColors) SyntaxHighlighting(buffer)
else buffer
- val ansiBuffer = Ansi.Str.parse(coloredBuffer)
+ val ansiBuffer = Ansi.Str.parse(coloredBuffer.toVector)
val (newBuffer, cursorOffset) = SelectionFilter.mangleBuffer(
selectionFilter, ansiBuffer, cursor, Ansi.Reversed.On
)
diff --git a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala
index 163bbea16..5b3669d5e 100644
--- a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala
+++ b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala
@@ -117,22 +117,22 @@ class CompilingInterpreter(
}
}
- private def newReporter = new ConsoleReporter(Console.in, out) {
- override def printMessage(msg: String) = {
- if (!delayOutput) {
- out.print(/*clean*/(msg) + "\n")
+ private def newReporter =
+ new ConsoleReporter(Console.in, out) {
+ override def printMessage(msg: String) =
+ if (!delayOutput) {
+ out.print(/*clean*/(msg) + "\n")
// Suppress clean for now for compiler messages
// Otherwise we will completely delete all references to
// line$object$ module classes. The previous interpreter did not
// have the project because the module class was written without the final `$'
// and therefore escaped the purge. We can turn this back on once
// we drop the final `$' from module classes.
- out.flush()
- } else {
- previousOutput += (/*clean*/(msg) + "\n")
- }
+ out.flush()
+ } else {
+ previousOutput += (/*clean*/(msg) + "\n")
+ }
}
- }
/** the previous requests this interpreter has processed */
private val prevRequests = new ArrayBuffer[Request]()
@@ -212,8 +212,10 @@ class CompilingInterpreter(
case None => Interpreter.Incomplete
case Some(Nil) => Interpreter.Error // parse error or empty input
case Some(tree :: Nil) if tree.isTerm && !tree.isInstanceOf[Assign] =>
+ previousOutput.clear() // clear previous error reporting
interpret(s"val $newVarName =\n$line")
case Some(trees) =>
+ previousOutput.clear() // clear previous error reporting
val req = new Request(line, newLineName)
if (!req.compile())
Interpreter.Error // an error happened during compilation, e.g. a type error
@@ -314,9 +316,13 @@ class CompilingInterpreter(
/** One line of code submitted by the user for interpretation */
private class Request(val line: String, val lineName: String)(implicit ctx: Context) {
- private val trees = parse(line) match {
- case Some(ts) => ts
- case None => Nil
+ private val trees = {
+ val parsed = parse(line)
+ previousOutput.clear() // clear previous error reporting
+ parsed match {
+ case Some(ts) => ts
+ case None => Nil
+ }
}
/** name to use for the object that will compute "line" */
diff --git a/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/src/dotty/tools/dotc/repl/InterpreterLoop.scala
index 7e5dcc7f1..8b1000f2e 100644
--- a/src/dotty/tools/dotc/repl/InterpreterLoop.scala
+++ b/src/dotty/tools/dotc/repl/InterpreterLoop.scala
@@ -83,25 +83,16 @@ class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Con
/** interpret all lines from a specified file */
def interpretAllFrom(filename: String): Unit = {
- val fileIn = try {
- new FileReader(filename)
- } catch {
- case _: IOException =>
- output.println("Error opening file: " + filename)
- return
- }
- val oldIn = in
- val oldReplay = replayCommandsRev
+ import java.nio.file.{Files, Paths}
+ import scala.collection.JavaConversions._
try {
- val inFile = new BufferedReader(fileIn)
- in = new SimpleReader(inFile, output, false)
+ val lines = Files.readAllLines(Paths.get(filename)).mkString("\n")
output.println("Loading " + filename + "...")
output.flush
- repl()
- } finally {
- in = oldIn
- replayCommandsRev = oldReplay
- fileIn.close
+ interpreter.interpret(lines)
+ } catch {
+ case _: IOException =>
+ output.println("Error opening file: " + filename)
}
}
diff --git a/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala b/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala
index ebbcf2148..faa97c348 100644
--- a/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala
+++ b/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala
@@ -25,12 +25,11 @@ object BasicFilters {
typingFilter
)
- def injectNewLine(b: Vector[Char], c: Int, rest: LazyList[Int]) = {
+ def injectNewLine(b: Vector[Char], c: Int, rest: LazyList[Int], indent: Int = 0) = {
val (first, last) = b.splitAt(c)
- TermState(rest, (first :+ '\n') ++ last, c + 1)
+ TermState(rest, (first :+ '\n') ++ last ++ Vector.fill(indent)(' '), c + 1 + indent)
}
-
def navFilter = Filter.merge(
Case(Up)((b, c, m) => moveUp(b, c, m.width)),
Case(Down)((b, c, m) => moveDown(b, c, m.width)),
diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
index deb772db5..da3df6984 100644
--- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
+++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
@@ -4,54 +4,142 @@ package reporting
import scala.collection.mutable
import util.SourcePosition
-import core.Contexts._
+import core.Contexts._, core.Decorators._
import Reporter._
import java.io.{ BufferedReader, IOException, PrintWriter }
import scala.reflect.internal.util._
+import printing.SyntaxHighlighting._
+import printing.Highlighting._
+import diagnostic.{ Message, MessageContainer, NoExplanation }
+import diagnostic.messages._
/**
- * This class implements a Reporter that displays messages on a text
- * console.
- */
+ * This class implements a Reporter that displays messages on a text console
+ */
class ConsoleReporter(
- reader: BufferedReader = Console.in,
- writer: PrintWriter = new PrintWriter(Console.err, true))
- extends Reporter with UniqueMessagePositions with HideNonSensicalMessages {
+ reader: BufferedReader = Console.in,
+ writer: PrintWriter = new PrintWriter(Console.err, true)
+) extends Reporter with UniqueMessagePositions with HideNonSensicalMessages {
+
+ import MessageContainer._
/** maximal number of error messages to be printed */
protected def ErrorLimit = 100
- def printPos(pos: SourcePosition): Unit =
- if (pos.exists) {
- printMessage(pos.lineContent.stripLineEnd)
- printMessage(" " * pos.column + "^")
- if (pos.outer.exists) {
- printMessage(s"\n... this location is in code that was inlined at ${pos.outer}:\n")
- printPos(pos.outer)
+ /** Prints the message. */
+ def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
+
+ def stripColor(str: String): String =
+ str.replaceAll("\u001B\\[[;\\d]*m", "")
+
+ def sourceLines(pos: SourcePosition)(implicit ctx: Context): (List[String], List[String], Int) = {
+ var maxLen = Int.MinValue
+ def render(xs: List[Int]) =
+ xs.map(pos.source.offsetToLine(_))
+ .map { lineNbr =>
+ val prefix = s"${lineNbr + 1} |"
+ maxLen = math.max(maxLen, prefix.length)
+ (prefix, pos.lineContent(lineNbr).stripLineEnd)
+ }
+ .map { case (prefix, line) =>
+ val lnum = Red(" " * math.max(0, maxLen - prefix.length) + prefix)
+ hl"$lnum$line"
}
+
+ val (before, after) = pos.beforeAndAfterPoint
+ (render(before), render(after), maxLen)
+ }
+
+ def columnMarker(pos: SourcePosition, offset: Int)(implicit ctx: Context) = {
+ val prefix = " " * (offset - 1)
+ val whitespace = " " * pos.startColumn
+ val carets = Red {
+ if (pos.startLine == pos.endLine)
+ "^" * math.max(1, pos.endColumn - pos.startColumn)
+ else "^"
}
- /** Prints the message. */
- def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
+ s"$prefix|$whitespace${carets.show}"
+ }
+
+ def errorMsg(pos: SourcePosition, msg: String, offset: Int)(implicit ctx: Context) = {
+ val leastWhitespace = msg.lines.foldLeft(Int.MaxValue) { (minPad, line) =>
+ val lineLength = stripColor(line).length
+ val padding =
+ math.min(math.max(0, ctx.settings.pageWidth.value - offset - lineLength), offset + pos.startColumn)
+
+ if (padding < minPad) padding
+ else minPad
+ }
+
+ msg.lines
+ .map { line => " " * (offset - 1) + "|" + (" " * (leastWhitespace - offset)) + line }
+ .mkString(sys.props("line.separator"))
+ }
+
+ def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(implicit ctx: Context) =
+ if (pos.exists) Blue({
+ val file = pos.source.file.toString
+ val errId =
+ if (message.errorId != NoExplanation.ID)
+ s"[E${"0" * (3 - message.errorId.toString.length) + message.errorId}] "
+ else ""
+ val kind =
+ if (message.kind == "") diagnosticLevel
+ else s"${message.kind} $diagnosticLevel"
+ val prefix = s"-- ${errId}${kind}: $file "
+
+ prefix +
+ ("-" * math.max(ctx.settings.pageWidth.value - stripColor(prefix).length, 0))
+ }).show else ""
/** Prints the message with the given position indication. */
- def printMessageAndPos(msg: String, pos: SourcePosition)(implicit ctx: Context): Unit = {
- val posStr = if (pos.exists) s"$pos: " else ""
- printMessage(posStr + msg)
- printPos(pos)
+ def printMessageAndPos(msg: Message, pos: SourcePosition, diagnosticLevel: String)(implicit ctx: Context): Boolean = {
+ printMessage(posStr(pos, diagnosticLevel, msg))
+ if (pos.exists) {
+ val (srcBefore, srcAfter, offset) = sourceLines(pos)
+ val marker = columnMarker(pos, offset)
+ val err = errorMsg(pos, msg.msg, offset)
+
+ printMessage((srcBefore ::: marker :: err :: srcAfter).mkString("\n"))
+ } else printMessage(msg.msg)
+ true
+ }
+
+ def printExplanation(m: Message)(implicit ctx: Context): Unit = {
+ printMessage(hl"""|
+ |${Blue("Explanation")}
+ |${Blue("===========")}""".stripMargin)
+ printMessage(m.explanation)
+ if (m.explanation.lastOption != Some('\n')) printMessage("")
}
- override def doReport(d: Diagnostic)(implicit ctx: Context): Unit = d match {
- case d: Error =>
- printMessageAndPos(s"error: ${d.message}", d.pos)
- if (ctx.settings.prompt.value) displayPrompt()
- case d: ConditionalWarning if !d.enablingOption.value =>
- case d: MigrationWarning =>
- printMessageAndPos(s"migration warning: ${d.message}", d.pos)
- case d: Warning =>
- printMessageAndPos(s"warning: ${d.message}", d.pos)
- case _ =>
- printMessageAndPos(d.message, d.pos)
+ override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
+ val didPrint = m match {
+ case m: Error =>
+ val didPrint = printMessageAndPos(m.contained, m.pos, "Error")
+ if (ctx.settings.prompt.value) displayPrompt()
+ didPrint
+ case m: ConditionalWarning if !m.enablingOption.value =>
+ false
+ case m: FeatureWarning =>
+ printMessageAndPos(m.contained, m.pos, "Feature Warning")
+ case m: DeprecationWarning =>
+ printMessageAndPos(m.contained, m.pos, "Deprecation Warning")
+ case m: UncheckedWarning =>
+ printMessageAndPos(m.contained, m.pos, "Unchecked Warning")
+ case m: MigrationWarning =>
+ printMessageAndPos(m.contained, m.pos, "Migration Warning")
+ case m: Warning =>
+ printMessageAndPos(m.contained, m.pos, "Warning")
+ case m: Info =>
+ printMessageAndPos(m.contained, m.pos, "Info")
+ }
+
+ if (didPrint && ctx.shouldExplain(m))
+ printExplanation(m.contained)
+ else if (didPrint && m.contained.explanation.nonEmpty)
+ printMessage("\nlonger explanation available when compiling with `-explain`")
}
def displayPrompt(): Unit = {
@@ -71,3 +159,4 @@ class ConsoleReporter(
override def flush()(implicit ctx: Context): Unit = { writer.flush() }
}
+
diff --git a/src/dotty/tools/dotc/reporting/Diagnostic.scala b/src/dotty/tools/dotc/reporting/Diagnostic.scala
deleted file mode 100644
index bcf55e993..000000000
--- a/src/dotty/tools/dotc/reporting/Diagnostic.scala
+++ /dev/null
@@ -1,47 +0,0 @@
-package dotty.tools
-package dotc
-package reporting
-
-import util.SourcePosition
-
-import java.util.Optional
-
-object Diagnostic {
- val nonSensicalStartTag = "<nonsensical>"
- val nonSensicalEndTag = "</nonsensical>"
-}
-
-class Diagnostic(msgFn: => String, val pos: SourcePosition, val level: Int)
- extends Exception with interfaces.Diagnostic {
- import Diagnostic._
- private var myMsg: String = null
- private var myIsNonSensical: Boolean = false
-
- override def position: Optional[interfaces.SourcePosition] =
- if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
-
- /** The message to report */
- def message: String = {
- if (myMsg == null) {
- myMsg = msgFn
- if (myMsg.contains(nonSensicalStartTag)) {
- myIsNonSensical = true
- // myMsg might be composed of several d"..." invocations -> nested nonsensical tags possible
- myMsg = myMsg.replaceAllLiterally(nonSensicalStartTag, "").replaceAllLiterally(nonSensicalEndTag, "")
- }
- }
- myMsg
- }
-
- /** A message is non-sensical if it contains references to <nonsensical> tags.
- * Such tags are inserted by the error diagnostic framework if a message
- * contains references to internally generated error types. Normally we
- * want to suppress error messages referring to types like this because
- * they look weird and are normally follow-up errors to something that
- * was diagnosed before.
- */
- def isNonSensical = { message; myIsNonSensical }
-
- override def toString = s"$getClass at $pos: $message"
- override def getMessage() = message
-}
diff --git a/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala b/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala
index a325fe9f7..ba1ab9b33 100644
--- a/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala
+++ b/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala
@@ -3,6 +3,7 @@ package dotc
package reporting
import core.Contexts.Context
+import diagnostic.MessageContainer
/**
* This trait implements `isHidden` so that we avoid reporting non-sensical messages.
@@ -11,9 +12,9 @@ trait HideNonSensicalMessages extends Reporter {
/** Hides non-sensical messages, unless we haven't reported any error yet or
* `-Yshow-suppressed-errors` is set.
*/
- override def isHidden(d: Diagnostic)(implicit ctx: Context): Boolean =
- super.isHidden(d) || {
- d.isNonSensical &&
+ override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
+ super.isHidden(m) || {
+ m.isNonSensical &&
hasErrors && // if there are no errors yet, report even if diagnostic is non-sensical
!ctx.settings.YshowSuppressedErrors.value
}
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index 75113d823..b38334412 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -4,44 +4,24 @@ package reporting
import core.Contexts._
import util.{SourcePosition, NoSourcePosition}
-import util.{SourceFile, NoSource}
import core.Decorators.PhaseListDecorator
import collection.mutable
-import config.Settings.Setting
import config.Printers
import java.lang.System.currentTimeMillis
import core.Mode
-import interfaces.Diagnostic.{ERROR, WARNING, INFO}
import dotty.tools.dotc.core.Symbols.Symbol
+import diagnostic.messages._
+import diagnostic._
+import Message._
object Reporter {
- class Error(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, ERROR)
- class Warning(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, WARNING)
- class Info(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, INFO)
-
- abstract class ConditionalWarning(msgFn: => String, pos: SourcePosition) extends Warning(msgFn, pos) {
- def enablingOption(implicit ctx: Context): Setting[Boolean]
- }
- class FeatureWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) {
- def enablingOption(implicit ctx: Context) = ctx.settings.feature
- }
- class UncheckedWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) {
- def enablingOption(implicit ctx: Context) = ctx.settings.unchecked
- }
- class DeprecationWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) {
- def enablingOption(implicit ctx: Context) = ctx.settings.deprecation
- }
- class MigrationWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) {
- def enablingOption(implicit ctx: Context) = ctx.settings.migration
- }
-
/** Convert a SimpleReporter into a real Reporter */
def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter =
new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
- override def doReport(d: Diagnostic)(implicit ctx: Context): Unit = d match {
- case d: ConditionalWarning if !d.enablingOption.value =>
+ override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match {
+ case m: ConditionalWarning if !m.enablingOption.value =>
case _ =>
- simple.report(d)
+ simple.report(m)
}
}
}
@@ -57,17 +37,17 @@ trait Reporting { this: Context =>
def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(new Info(msg, pos))
- def deprecationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new DeprecationWarning(msg, pos))
+ def deprecationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.deprecationWarning(pos))
- def migrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new MigrationWarning(msg, pos))
+ def migrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.migrationWarning(pos))
- def uncheckedWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new UncheckedWarning(msg, pos))
+ def uncheckedWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.uncheckedWarning(pos))
- def featureWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new FeatureWarning(msg, pos))
+ def featureWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.featureWarning(pos))
def featureWarning(feature: String, featureDescription: String, isScala2Feature: Boolean,
featureUseSite: Symbol, required: Boolean, pos: SourcePosition): Unit = {
@@ -92,26 +72,24 @@ trait Reporting { this: Context =>
else reporter.report(new FeatureWarning(msg, pos))
}
- def warning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new Warning(msg, pos))
+ def warning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.warning(pos))
- def strictWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
+ def strictWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
if (this.settings.strict.value) error(msg, pos)
- else warning(msg + "\n(This would be an error under strict mode)", pos)
+ else warning(msg.mapMsg(_ + "\n(This would be an error under strict mode)"), pos)
- def error(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = {
- // println("*** ERROR: " + msg) // !!! DEBUG
- reporter.report(new Error(msg, pos))
- }
+ def error(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.error(pos))
- def errorOrMigrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
+ def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos)
- def restrictionError(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- error(s"Implementation restriction: $msg", pos)
+ def restrictionError(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ error(msg.mapMsg(m => s"Implementation restriction: $m"), pos)
- def incompleteInputError(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
- reporter.incomplete(new Error(msg, pos))(ctx)
+ def incompleteInputError(msg: Message, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
+ reporter.incomplete(msg.error(pos))(ctx)
/** Log msg if settings.log contains the current phase.
* See [[config.CompilerCommand#explainAdvanced]] for the exact meaning of
@@ -198,7 +176,7 @@ trait Reporting { this: Context =>
abstract class Reporter extends interfaces.ReporterResult {
/** Report a diagnostic */
- def doReport(d: Diagnostic)(implicit ctx: Context): Unit
+ def doReport(d: MessageContainer)(implicit ctx: Context): Unit
/** Whether very long lines can be truncated. This exists so important
* debugging information (like printing the classpath) is not rendered
@@ -213,7 +191,7 @@ abstract class Reporter extends interfaces.ReporterResult {
finally _truncationOK = saved
}
- type ErrorHandler = Diagnostic => Context => Unit
+ type ErrorHandler = MessageContainer => Context => Unit
private var incompleteHandler: ErrorHandler = d => c => report(d)(c)
def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
val saved = incompleteHandler
@@ -242,7 +220,7 @@ abstract class Reporter extends interfaces.ReporterResult {
override def default(key: String) = 0
}
- def report(d: Diagnostic)(implicit ctx: Context): Unit =
+ def report(d: MessageContainer)(implicit ctx: Context): Unit =
if (!isHidden(d)) {
doReport(d)(ctx.addMode(Mode.Printing))
d match {
@@ -256,10 +234,9 @@ abstract class Reporter extends interfaces.ReporterResult {
}
}
- def incomplete(d: Diagnostic)(implicit ctx: Context): Unit =
+ def incomplete(d: MessageContainer)(implicit ctx: Context): Unit =
incompleteHandler(d)(ctx)
-
/** Summary of warnings and errors */
def summary: String = {
val b = new mutable.ListBuffer[String]
@@ -279,7 +256,7 @@ abstract class Reporter extends interfaces.ReporterResult {
}
/** Returns a string meaning "n elements". */
- private def countString(n: Int, elements: String): String = n match {
+ protected def countString(n: Int, elements: String): String = n match {
case 0 => "no " + elements + "s"
case 1 => "one " + elements
case 2 => "two " + elements + "s"
@@ -289,7 +266,7 @@ abstract class Reporter extends interfaces.ReporterResult {
}
/** Should this diagnostic not be reported at all? */
- def isHidden(d: Diagnostic)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing)
+ def isHidden(m: MessageContainer)(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/reporting/StoreReporter.scala b/src/dotty/tools/dotc/reporting/StoreReporter.scala
index b7b7c1af0..e85017ed2 100644
--- a/src/dotty/tools/dotc/reporting/StoreReporter.scala
+++ b/src/dotty/tools/dotc/reporting/StoreReporter.scala
@@ -4,26 +4,27 @@ package reporting
import core.Contexts.Context
import collection.mutable
-import Reporter.{Error, Warning}
import config.Printers.typr
+import diagnostic.MessageContainer
+import diagnostic.messages._
/**
* This class implements a Reporter that stores all messages
*/
class StoreReporter(outer: Reporter) extends Reporter {
- private var infos: mutable.ListBuffer[Diagnostic] = null
+ private var infos: mutable.ListBuffer[MessageContainer] = null
- def doReport(d: Diagnostic)(implicit ctx: Context): Unit = {
- typr.println(s">>>> StoredError: ${d.message}") // !!! DEBUG
+ def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
+ typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG
if (infos == null) infos = new mutable.ListBuffer
- infos += d
+ infos += m
}
override def hasPending: Boolean = infos != null && {
infos exists {
- case d: Error => true
- case d: Warning => true
+ case _: Error => true
+ case _: Warning => true
case _ => false
}
}
diff --git a/src/dotty/tools/dotc/reporting/ThrowingReporter.scala b/src/dotty/tools/dotc/reporting/ThrowingReporter.scala
index 026453036..d8e03ab66 100644
--- a/src/dotty/tools/dotc/reporting/ThrowingReporter.scala
+++ b/src/dotty/tools/dotc/reporting/ThrowingReporter.scala
@@ -4,6 +4,8 @@ package reporting
import core.Contexts.Context
import collection.mutable
+import diagnostic.MessageContainer
+import diagnostic.messages.Error
import Reporter._
/**
@@ -11,8 +13,8 @@ import Reporter._
* info to the underlying reporter.
*/
class ThrowingReporter(reportInfo: Reporter) extends Reporter {
- def doReport(d: Diagnostic)(implicit ctx: Context): Unit = d match {
- case _: Error => throw d
- case _ => reportInfo.doReport(d)
+ def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match {
+ case _: Error => throw m
+ case _ => reportInfo.doReport(m)
}
}
diff --git a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
index 32554e6b6..6fd971c2a 100644
--- a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
+++ b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
@@ -5,11 +5,10 @@ package reporting
import scala.collection.mutable
import util.{SourcePosition, SourceFile}
import core.Contexts.Context
+import diagnostic.MessageContainer
-/**
- * This trait implements `isHidden` so that multiple messages per position
- * are suppressed, unless they are of increasing severity.
- */
+/** This trait implements `isHidden` so that multiple messages per position
+ * are suppressed, unless they are of increasing severity. */
trait UniqueMessagePositions extends Reporter {
private val positions = new mutable.HashMap[(SourceFile, Int), Int]
@@ -17,13 +16,17 @@ trait UniqueMessagePositions extends Reporter {
/** Logs a position and returns true if it was already logged.
* @note Two positions are considered identical for logging if they have the same point.
*/
- override def isHidden(d: Diagnostic)(implicit ctx: Context): Boolean =
- super.isHidden(d) || {
- d.pos.exists && {
- positions get (ctx.source, d.pos.point) match {
- case Some(level) if level >= d.level => true
- case _ => positions((ctx.source, d.pos.point)) = d.level; false
+ override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
+ super.isHidden(m) || {
+ m.pos.exists && {
+ var shouldHide = false
+ for (pos <- m.pos.start to m.pos.end) {
+ positions get (ctx.source, pos) match {
+ case Some(level) if level >= m.level => shouldHide = true
+ case _ => positions((ctx.source, pos)) = m.level
+ }
}
+ shouldHide
}
}
}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
new file mode 100644
index 000000000..8b1f65673
--- /dev/null
+++ b/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
@@ -0,0 +1,106 @@
+package dotty.tools
+package dotc
+package reporting
+package diagnostic
+
+import util.SourcePosition
+import core.Contexts.Context
+
+object Message {
+ /** This implicit conversion provides a fallback for error messages that have
+ * not yet been ported to the new scheme. Comment out this `implicit def` to
+ * see where old errors still exist
+ */
+ implicit def toNoExplanation(str: String): Message =
+ new NoExplanation(str)
+}
+
+/** A `Message` contains all semantic information necessary to easily
+ * comprehend what caused the message to be logged. Each message can be turned
+ * into a `MessageContainer` which contains the log level and can later be
+ * consumed by a subclass of `Reporter`.
+ *
+ * @param errorId a unique number identifying the message, this will later be
+ * used to reference documentation online
+ */
+abstract class Message(val errorId: Int) { self =>
+ import messages._
+
+ /** The `msg` contains the diagnostic message e.g:
+ *
+ * > expected: String
+ * > found: Int
+ *
+ * This message wil be placed underneath the position given by the enclosing
+ * `MessageContainer`
+ */
+ def msg: String
+
+ /** The kind of the error message is something like "Syntax" or "Type
+ * Mismatch"
+ */
+ def kind: String
+
+ /** The explanation should provide a detailed description of why the error
+ * occurred and use examples from the user's own code to illustrate how to
+ * avoid these errors.
+ */
+ def explanation: String
+
+ /** It is possible to map `msg` to add details, this is at the loss of
+ * precision since the type of the resulting `Message` won't be original
+ * extending class
+ *
+ * @return a `Message` with the mapped message
+ */
+ def mapMsg(f: String => String) = new Message(errorId) {
+ val msg = f(self.msg)
+ val kind = self.kind
+ val explanation = self.explanation
+ }
+
+ /** Enclose this message in an `Error` container */
+ def error(pos: SourcePosition) =
+ new Error(self, pos)
+
+ /** Enclose this message in an `Warning` container */
+ def warning(pos: SourcePosition) =
+ new Warning(self, pos)
+
+ /** Enclose this message in an `Info` container */
+ def info(pos: SourcePosition) =
+ new Info(self, pos)
+
+ /** Enclose this message in an `FeatureWarning` container */
+ def featureWarning(pos: SourcePosition) =
+ new FeatureWarning(self, pos)
+
+ /** Enclose this message in an `UncheckedWarning` container */
+ def uncheckedWarning(pos: SourcePosition) =
+ new UncheckedWarning(self, pos)
+
+ /** Enclose this message in an `DeprecationWarning` container */
+ def deprecationWarning(pos: SourcePosition) =
+ new DeprecationWarning(self, pos)
+
+ /** Enclose this message in an `MigrationWarning` container */
+ def migrationWarning(pos: SourcePosition) =
+ new MigrationWarning(self, pos)
+}
+
+/** The fallback `Message` containing no explanation and having no `kind` */
+class NoExplanation(val msg: String) extends Message(NoExplanation.ID) {
+ val explanation = ""
+ val kind = ""
+}
+
+/** The extractor for `NoExplanation` can be used to check whether any error
+ * lacks an explanation
+ */
+object NoExplanation {
+ final val ID = -1
+
+ def unapply(m: Message): Option[Message] =
+ if (m.explanation == "") Some(m)
+ else None
+}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala b/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala
new file mode 100644
index 000000000..7fd50bfdc
--- /dev/null
+++ b/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala
@@ -0,0 +1,74 @@
+package dotty.tools
+package dotc
+package reporting
+package diagnostic
+
+import util.SourcePosition
+import core.Contexts.Context
+
+import java.util.Optional
+
+object MessageContainer {
+ val nonSensicalStartTag = "<nonsensical>"
+ val nonSensicalEndTag = "</nonsensical>"
+
+ implicit class MessageContext(val c: Context) extends AnyVal {
+ def shouldExplain(cont: MessageContainer): Boolean = {
+ implicit val ctx: Context = c
+ cont.contained.explanation match {
+ case "" => false
+ case _ => ctx.settings.explain.value
+ }
+ }
+ }
+}
+
+class MessageContainer(
+ msgFn: => Message,
+ val pos: SourcePosition,
+ val level: Int
+) extends Exception with interfaces.Diagnostic {
+ import MessageContainer._
+ private var myMsg: String = null
+ private var myIsNonSensical: Boolean = false
+ private var myContained: Message = null
+
+ override def position: Optional[interfaces.SourcePosition] =
+ if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
+
+ /** The message to report */
+ def message: String = {
+ if (myMsg == null) {
+ myMsg = contained.msg.replaceAll("\u001B\\[[;\\d]*m", "")
+ if (myMsg.contains(nonSensicalStartTag)) {
+ myIsNonSensical = true
+ // myMsg might be composed of several d"..." invocations -> nested
+ // nonsensical tags possible
+ myMsg =
+ myMsg
+ .replaceAllLiterally(nonSensicalStartTag, "")
+ .replaceAllLiterally(nonSensicalEndTag, "")
+ }
+ }
+ myMsg
+ }
+
+ def contained: Message = {
+ if (myContained == null)
+ myContained = msgFn
+
+ myContained
+ }
+
+ /** A message is non-sensical if it contains references to <nonsensical>
+ * tags. Such tags are inserted by the error diagnostic framework if a
+ * message contains references to internally generated error types. Normally
+ * we want to suppress error messages referring to types like this because
+ * they look weird and are normally follow-up errors to something that was
+ * diagnosed before.
+ */
+ def isNonSensical = { message; myIsNonSensical }
+
+ override def toString = s"$getClass at $pos: ${message}"
+ override def getMessage() = message
+}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
new file mode 100644
index 000000000..9a02e0b04
--- /dev/null
+++ b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -0,0 +1,321 @@
+package dotty.tools
+package dotc
+package reporting
+package diagnostic
+
+import dotc.core._
+import Contexts.Context, Decorators._, Symbols._, Names._, Types._
+import util.{SourceFile, NoSource}
+import util.{SourcePosition, NoSourcePosition}
+import config.Settings.Setting
+import interfaces.Diagnostic.{ERROR, WARNING, INFO}
+import printing.SyntaxHighlighting._
+import printing.Formatting
+
+object messages {
+
+ // `MessageContainer`s to be consumed by `Reporter` ---------------------- //
+ class Error(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends MessageContainer(msgFn, pos, ERROR)
+
+ class Warning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends MessageContainer(msgFn, pos, WARNING)
+
+ class Info(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends MessageContainer(msgFn, pos, INFO)
+
+ abstract class ConditionalWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends Warning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context): Setting[Boolean]
+ }
+
+ class FeatureWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.feature
+ }
+
+ class UncheckedWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.unchecked
+ }
+
+ class DeprecationWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.deprecation
+ }
+
+ class MigrationWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.migration
+ }
+
+ /** Messages
+ * ========
+ * The role of messages is to provide the necessary details for a simple to
+ * understand diagnostic event. Each message can be turned into a message
+ * container (one of the above) by calling the appropriate method on them.
+ * For instance:
+ *
+ * ```scala
+ * EmptyCatchBlock(tree).error(pos) // res: Error
+ * EmptyCatchBlock(tree).warning(pos) // res: Warning
+ * ```
+ */
+ import dotc.ast.Trees._
+ import dotc.ast.untpd
+
+ // Syntax Errors ---------------------------------------------------------- //
+ abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: Int)(implicit ctx: Context)
+ extends Message(errNo) {
+ val explanation = {
+ val tryString = tryBody match {
+ case Block(Nil, untpd.EmptyTree) => "{}"
+ case _ => tryBody.show
+ }
+
+ val code1 =
+ s"""|import scala.util.control.NonFatal
+ |
+ |try $tryString catch {
+ | case NonFatal(e) => ???
+ |}""".stripMargin
+
+ val code2 =
+ s"""|try $tryString finally {
+ | // perform your cleanup here!
+ |}""".stripMargin
+
+ hl"""|A ${"try"} expression should be followed by some mechanism to handle any exceptions
+ |thrown. Typically a ${"catch"} expression follows the ${"try"} and pattern matches
+ |on any expected exceptions. For example:
+ |
+ |$code1
+ |
+ |It is also possible to follow a ${"try"} immediately by a ${"finally"} - letting the
+ |exception propagate - but still allowing for some clean up in ${"finally"}:
+ |
+ |$code2
+ |
+ |It is recommended to use the ${"NonFatal"} extractor to catch all exceptions as it
+ |correctly handles transfer functions like ${"return"}.""".stripMargin
+ }
+ }
+
+ case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context)
+ extends EmptyCatchOrFinallyBlock(tryBody, 1) {
+ val kind = "Syntax"
+ val msg =
+ hl"""|The ${"catch"} block does not contain a valid expression, try
+ |adding a case like - `${"case e: Exception =>"}` to the block""".stripMargin
+ }
+
+ case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context)
+ extends EmptyCatchOrFinallyBlock(tryBody, 2) {
+ val kind = "Syntax"
+ val msg =
+ hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting
+ |its body in a block; no exceptions are handled.""".stripMargin
+ }
+
+ case class DeprecatedWithOperator()(implicit ctx: Context)
+ extends Message(3) {
+ val kind = "Syntax"
+ val msg =
+ hl"""${"with"} as a type operator has been deprecated; use `&' instead"""
+ val explanation =
+ hl"""|Dotty introduces intersection types - `&' types. These replace the
+ |use of the ${"with"} keyword. There are a few differences in
+ |semantics between intersection types and using `${"with"}'.""".stripMargin
+ }
+
+ case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context)
+ extends Message(4) {
+ val kind = "Syntax"
+ val msg =
+ hl"""|A ${"case class"} must have at least one parameter list"""
+
+ val explanation =
+ hl"""|${cdef.name} must have at least one parameter list, if you would rather
+ |have a singleton representation of ${cdef.name}, use a "${"case object"}".
+ |Or, add an explicit `()' as a parameter list to ${cdef.name}.""".stripMargin
+ }
+
+
+ // Type Errors ------------------------------------------------------------ //
+ case class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context)
+ extends Message(5) {
+ val kind = "Naming"
+ val msg = em"duplicate pattern variable: `${bind.name}`"
+
+ val explanation = {
+ val pat = tree.pat.show
+ val guard = tree.guard match {
+ case untpd.EmptyTree => ""
+ case guard => s"if ${guard.show}"
+ }
+
+ val body = tree.body match {
+ case Block(Nil, untpd.EmptyTree) => ""
+ case body => s" ${body.show}"
+ }
+
+ val caseDef = s"case $pat$guard => $body"
+
+ hl"""|For each ${"case"} bound variable names have to be unique. In:
+ |
+ |$caseDef
+ |
+ |`${bind.name}` is not unique. Rename one of the bound variables!""".stripMargin
+ }
+ }
+
+ case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context)
+ extends Message(6) {
+ val kind = "Missing Identifier"
+ val msg = em"not found: $treeKind$name"
+
+ val explanation = {
+ hl"""|An identifier for `$treeKind$name` is missing. This means that something
+ |has either been misspelt or you're forgetting an import""".stripMargin
+ }
+ }
+
+ case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "", implicitFailure: String = "")(implicit ctx: Context)
+ extends Message(7) {
+ val kind = "Type Mismatch"
+ val msg = {
+ val (where, printCtx) = Formatting.disambiguateTypes(found, expected)
+ val (fnd, exp) = Formatting.typeDiff(found, expected)(printCtx)
+ s"""|found: $fnd
+ |required: $exp
+ |
+ |$where""".stripMargin + whyNoMatch + implicitFailure
+ }
+
+ val explanation = ""
+ }
+
+ case class NotAMember(site: Type, name: Name, selected: String)(implicit ctx: Context)
+ extends Message(8) {
+ val kind = "Member Not Found"
+
+ val msg = {
+ import core.Flags._
+ val maxDist = 3
+ val decls = site.decls.flatMap { sym =>
+ if (sym.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil
+ else List((sym.name.show, sym))
+ }
+
+ // Calculate Levenshtein distance
+ def distance(n1: Iterable[_], n2: Iterable[_]) =
+ n1.foldLeft(List.range(0, n2.size)) { (prev, x) =>
+ (prev zip prev.tail zip n2).scanLeft(prev.head + 1) {
+ case (h, ((d, v), y)) => math.min(
+ math.min(h + 1, v + 1),
+ if (x == y) d else d + 1
+ )
+ }
+ }.last
+
+ // Count number of wrong characters
+ def incorrectChars(x: (String, Int, Symbol)): (String, Symbol, Int) = {
+ val (currName, _, sym) = x
+ val matching = name.show.zip(currName).foldLeft(0) {
+ case (acc, (x,y)) => if (x != y) acc + 1 else acc
+ }
+ (currName, sym, matching)
+ }
+
+ // Get closest match in `site`
+ val closest =
+ decls
+ .map { case (n, sym) => (n, distance(n, name.show), sym) }
+ .collect { case (n, dist, sym) if dist <= maxDist => (n, dist, sym) }
+ .groupBy(_._2).toList
+ .sortBy(_._1)
+ .headOption.map(_._2).getOrElse(Nil)
+ .map(incorrectChars).toList
+ .sortBy(_._3)
+ .take(1).map { case (n, sym, _) => (n, sym) }
+
+ val siteName = site match {
+ case site: NamedType => site.name.show
+ case site => i"$site"
+ }
+
+ val closeMember = closest match {
+ case (n, sym) :: Nil => hl""" - did you mean `${s"$siteName.$n"}`?"""
+ case Nil => ""
+ case _ => assert(
+ false,
+ "Could not single out one distinct member to match on input with"
+ )
+ }
+
+ ex"$selected `$name` is not a member of $site$closeMember"
+ }
+
+ val explanation = ""
+ }
+
+ case class EarlyDefinitionsNotSupported()(implicit ctx:Context) extends Message(9) {
+ val kind = "Syntax"
+
+ val msg = "early definitions are not supported; use trait parameters instead"
+
+ val code1 =
+ """|trait Logging {
+ | val f: File
+ | f.open()
+ | onExit(f.close())
+ | def log(msg: String) = f.write(msg)
+ |}
+ |
+ |class B extends Logging {
+ | val f = new File("log.data") // triggers a null pointer exception
+ |}
+ |
+ |class C extends {
+ | val f = new File("log.data") // early definition gets around the null pointer exception
+ |} with Logging""".stripMargin
+
+ val code2 =
+ """|trait Logging(f: File) {
+ | f.open()
+ | onExit(f.close())
+ | def log(msg: String) = f.write(msg)
+ |}
+ |
+ |class C extends Logging(new File("log.data"))""".stripMargin
+
+ val explanation =
+ hl"""Earlier versions of Scala did not support trait parameters and "early definitions" (also known as "early initializers")
+ |were used as an alternative.
+ |
+ |Example of old syntax:
+ |
+ |$code1
+ |
+ |The above code can now be written as:
+ |
+ |$code2
+ |""".stripMargin
+ }
+}
diff --git a/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/src/dotty/tools/dotc/sbt/ExtractAPI.scala
index a7b18b6d6..bc8528c05 100644
--- a/src/dotty/tools/dotc/sbt/ExtractAPI.scala
+++ b/src/dotty/tools/dotc/sbt/ExtractAPI.scala
@@ -363,7 +363,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
val apiTycon = simpleType(tycon)
val apiArgs = args.map(processArg)
new api.Parameterized(apiTycon, apiArgs.toArray)
- case TypeLambda(tparams, res) =>
+ case PolyType(tparams, res) =>
val apiTparams = tparams.map(apiTypeParameter)
val apiRes = apiType(res)
new api.Polymorphic(apiRes, apiTparams.toArray)
diff --git a/src/dotty/tools/dotc/transform/ExplicitSelf.scala b/src/dotty/tools/dotc/transform/ExplicitSelf.scala
index 618a0f108..7bb65e575 100644
--- a/src/dotty/tools/dotc/transform/ExplicitSelf.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitSelf.scala
@@ -20,12 +20,21 @@ import Flags._
*
* where `S` is the self type of `C`.
* See run/i789.scala for a test case why this is needed.
+ *
+ * Also replaces idents referring to the self type with ThisTypes.
*/
class ExplicitSelf extends MiniPhaseTransform { thisTransform =>
import ast.tpd._
override def phaseName = "explicitSelf"
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match {
+ case tp: ThisType =>
+ ctx.debuglog(s"owner = ${ctx.owner}, context = ${ctx}")
+ This(tp.cls) withPos tree.pos
+ case _ => tree
+ }
+
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
case Select(thiz: This, name) if name.isTermName =>
val cls = thiz.symbol.asClass
diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala
index d2052d8cb..6c69c735b 100644
--- a/src/dotty/tools/dotc/transform/FullParameterization.scala
+++ b/src/dotty/tools/dotc/transform/FullParameterization.scala
@@ -111,13 +111,13 @@ trait FullParameterization {
}
/** Replace class type parameters by the added type parameters of the polytype `pt` */
- def mapClassParams(tp: Type, pt: GenericType): Type = {
+ def mapClassParams(tp: Type, pt: PolyType): Type = {
val classParamsRange = (mtparamCount until mtparamCount + ctparams.length).toList
tp.substDealias(ctparams, classParamsRange map (PolyParam(pt, _)))
}
/** The bounds for the added type parameters of the polytype `pt` */
- def mappedClassBounds(pt: GenericType): List[TypeBounds] =
+ def mappedClassBounds(pt: PolyType): List[TypeBounds] =
ctparams.map(tparam => mapClassParams(tparam.info, pt).bounds)
info match {
diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala
index 18b030913..19fb3dd0c 100644
--- a/src/dotty/tools/dotc/transform/LambdaLift.scala
+++ b/src/dotty/tools/dotc/transform/LambdaLift.scala
@@ -121,7 +121,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
private def symSet(f: LinkedHashMap[Symbol, SymSet], sym: Symbol): SymSet =
f.getOrElseUpdate(sym, newSymSet)
- def freeVars(sym: Symbol): List[Symbol] = free.getOrElse(sym, Nil).toList
+ def freeVars(sym: Symbol): List[Symbol] = free get sym match {
+ case Some(set) => set.toList
+ case None => Nil
+ }
def proxyOf(sym: Symbol, fv: Symbol) = proxyMap.getOrElse(sym, Map.empty)(fv)
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 49c0eabec..8636d5084 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -240,17 +240,21 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {
val isDefined = extractorMemberType(prev.tpe, nme.isDefined)
if ((isDefined isRef defn.BooleanClass) && getTp.exists) {
- val tmpSym = freshSym(prev.pos, prev.tpe, "o")
- val prevValue = ref(tmpSym).select("get".toTermName).ensureApplied
+ // isDefined and get may be overloaded
+ val getDenot = prev.tpe.member(nme.get).suchThat(_.info.isParameterless)
+ val isDefinedDenot = prev.tpe.member(nme.isDefined).suchThat(_.info.isParameterless)
- Block(
- List(ValDef(tmpSym, prev)),
- // must be isEmpty and get as we don't control the target of the call (prev is an extractor call)
- ifThenElseZero(
- ref(tmpSym).select(nme.isDefined),
- Block(List(ValDef(b.asTerm, prevValue)), next)
- )
+ val tmpSym = freshSym(prev.pos, prev.tpe, "o")
+ val prevValue = ref(tmpSym).select(getDenot.symbol).ensureApplied
+
+ Block(
+ List(ValDef(tmpSym, prev)),
+ // must be isEmpty and get as we don't control the target of the call (prev is an extractor call)
+ ifThenElseZero(
+ ref(tmpSym).select(isDefinedDenot.symbol),
+ Block(List(ValDef(b.asTerm, prevValue)), next)
)
+ )
} else {
assert(defn.isProductSubType(prev.tpe))
val nullCheck: Tree = prev.select(defn.Object_ne).appliedTo(Literal(Constant(null)))
diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala
index efcf95ede..d62be1a82 100644
--- a/src/dotty/tools/dotc/transform/Splitter.scala
+++ b/src/dotty/tools/dotc/transform/Splitter.scala
@@ -6,25 +6,34 @@ import ast.Trees._
import core._
import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotations._, Names._
-/** This transform makes sure every identifier and select node
- * carries a symbol. To do this, certain qualifiers with a union type
- * have to be "splitted" with a type test.
- *
- * For now, only self references are treated.
+/** Distribute applications into Block and If nodes
*/
class Splitter extends MiniPhaseTransform { thisTransform =>
import ast.tpd._
override def phaseName: String = "splitter"
- /** Replace self referencing idents with ThisTypes. */
- override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match {
- case tp: ThisType =>
- ctx.debuglog(s"owner = ${ctx.owner}, context = ${ctx}")
- This(tp.cls) withPos tree.pos
- case _ => tree
+ /** Distribute arguments among splitted branches */
+ def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = {
+ def recur(fn: Tree): Tree = fn match {
+ case Block(stats, expr) => Block(stats, recur(expr))
+ case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep))
+ case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos
+ }
+ recur(tree.fun)
}
+ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) =
+ distribute(tree, typeApply)
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
+ distribute(tree, apply)
+
+ private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx)
+ private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx)
+
+/* The following is no longer necessary, since we select members on the join of an or type:
+ *
/** If we select a name, make sure the node has a symbol.
* If necessary, split the qualifier with type tests.
* Example: Assume:
@@ -108,23 +117,5 @@ class Splitter extends MiniPhaseTransform { thisTransform =>
evalOnce(qual)(qual => choose(qual, candidates(qual.tpe)))
}
}
-
- /** Distribute arguments among splitted branches */
- def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = {
- def recur(fn: Tree): Tree = fn match {
- case Block(stats, expr) => Block(stats, recur(expr))
- case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep))
- case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos
- }
- recur(tree.fun)
- }
-
- override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) =
- distribute(tree, typeApply)
-
- override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
- distribute(tree, apply)
-
- private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx)
- private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx)
+*/
}
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index b345dda61..d99a48af3 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -145,17 +145,22 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
})
Block(List(labelDef), ref(label).appliedToArgss(vparamss0.map(_.map(x=> ref(x.symbol)))))
}} else {
- if (mandatory)
- ctx.error("TailRec optimisation not applicable, method not tail recursive", dd.pos)
+ if (mandatory) ctx.error(
+ "TailRec optimisation not applicable, method not tail recursive",
+ // FIXME: want to report this error on `dd.namePos`, but
+ // because of extension method getting a weird pos, it is
+ // better to report on symbol so there's no overlap
+ sym.pos
+ )
dd.rhs
}
})
}
case d: DefDef if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) =>
- ctx.error("TailRec optimisation not applicable, method is neither private nor final so can be overridden", d.pos)
+ ctx.error("TailRec optimisation not applicable, method is neither private nor final so can be overridden", sym.pos)
d
case d if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) =>
- ctx.error("TailRec optimisation not applicable, not a method", d.pos)
+ ctx.error("TailRec optimisation not applicable, not a method", sym.pos)
d
case _ => tree
}
@@ -247,7 +252,10 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
} else targs
val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef)
- val thisPassed = if(this.method.owner.isClass) method appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) else method
+ val thisPassed =
+ if (this.method.owner.isClass)
+ method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head))
+ else method
val res =
if (thisPassed.tpe.widen.isParameterless) thisPassed
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index 4b3927ccf..808178369 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -133,10 +133,7 @@ class TreeChecker extends Phase with SymTransformer {
catch {
case NonFatal(ex) => //TODO CHECK. Check that we are bootstrapped
implicit val ctx: Context = checkingCtx
- ctx.echo(i"*** error while checking ${ctx.compilationUnit} after phase ${checkingCtx.phase.prev} ***")
- ctx.echo(ex.toString)
- ctx.echo(ex.getStackTrace.take(30).deep.mkString("\n"))
- ctx.echo("<<<")
+ println(i"*** error while checking ${ctx.compilationUnit} after phase ${checkingCtx.phase.prev} ***")
throw ex
}
}
@@ -331,8 +328,30 @@ class TreeChecker extends Phase with SymTransformer {
checkNotRepeated(super.typedIdent(tree, pt))
}
+ /** Makes sure the symbol in the tree can be approximately reconstructed by
+ * calling `member` on the qualifier type.
+ * Approximately means: The two symbols might be different but one still overrides the other.
+ */
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase)
+ val tpe = tree.typeOpt
+ val sym = tree.symbol
+ if (!tpe.isInstanceOf[WithFixedSym] && sym.exists && !sym.is(Private)) {
+ val qualTpe = tree.qualifier.typeOpt
+ val member =
+ if (sym.is(Private)) qualTpe.member(tree.name)
+ else qualTpe.nonPrivateMember(tree.name)
+ val memberSyms = member.alternatives.map(_.symbol)
+ assert(memberSyms.exists(mbr =>
+ sym == mbr ||
+ sym.overriddenSymbol(mbr.owner.asClass) == mbr ||
+ mbr.overriddenSymbol(sym.owner.asClass) == sym),
+ ex"""symbols differ for $tree
+ |was : $sym
+ |alternatives by type: $memberSyms%, % of types ${memberSyms.map(_.info)}%, %
+ |qualifier type : ${tree.qualifier.typeOpt}
+ |tree type : ${tree.typeOpt} of class ${tree.typeOpt.getClass}""")
+ }
checkNotRepeated(super.typedSelect(tree, pt))
}
@@ -425,10 +444,11 @@ class TreeChecker extends Phase with SymTransformer {
!tree.isEmpty &&
!isPrimaryConstructorReturn &&
!pt.isInstanceOf[FunProto])
- assert(tree.tpe <:< pt,
- i"""error at ${sourcePos(tree.pos)}
- |${err.typeMismatchStr(tree.tpe, pt)}
- |tree = $tree""")
+ assert(tree.tpe <:< pt, {
+ val mismatch = err.typeMismatchMsg(tree.tpe, pt)
+ i"""|${mismatch.msg}
+ |tree = $tree""".stripMargin
+ })
tree
}
}
diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala
index 52a3ad94e..45fa3d607 100644
--- a/src/dotty/tools/dotc/transform/TreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/TreeTransform.scala
@@ -42,7 +42,7 @@ object TreeTransforms {
* the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with
* 0.2sec, or roughly 600M processor cycles.
*
- * Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking.
+ * Now, to the amount of work that needs to be done. The codebase produces an average of about 250'000 trees after typechecking.
* Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100
* micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison,
* scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits
@@ -208,7 +208,7 @@ object TreeTransforms {
if (cls.getDeclaredMethods.exists(_.getName == name)) cls != classOf[TreeTransform]
else hasRedefinedMethod(cls.getSuperclass, name)
- /** Create an index array `next` of size one larger than teh size of `transforms` such that
+ /** Create an index array `next` of size one larger than the size of `transforms` such that
* for each index i, `next(i)` is the smallest index j such that
*
* i <= j
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 2c9039db1..56595a637 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -27,6 +27,7 @@ import collection.mutable
import config.Printers.{typr, unapp, overload}
import TypeApplications._
import language.implicitConversions
+import reporting.diagnostic.Message
object Applications {
import tpd._
@@ -132,10 +133,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
protected def harmonizeArgs(args: List[TypedArg]): List[TypedArg]
/** Signal failure with given message at position of given argument */
- protected def fail(msg: => String, arg: Arg): Unit
+ protected def fail(msg: => Message, arg: Arg): Unit
/** Signal failure with given message at position of the application itself */
- protected def fail(msg: => String): Unit
+ protected def fail(msg: => Message): Unit
protected def appPos: Position
@@ -186,7 +187,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
// it might be healed by an implicit conversion
assert(ctx.typerState.constraint eq savedConstraint)
else
- fail(err.typeMismatchStr(methType.resultType, resultType))
+ fail(err.typeMismatchMsg(methType.resultType, resultType))
}
// match all arguments with corresponding formal parameters
matchArgs(orderedArgs, methType.paramTypes, 0)
@@ -388,9 +389,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
def addArg(arg: TypedArg, formal: Type) =
ok = ok & isCompatible(argType(arg, formal), formal)
def makeVarArg(n: Int, elemFormal: Type) = {}
- def fail(msg: => String, arg: Arg) =
+ def fail(msg: => Message, arg: Arg) =
ok = false
- def fail(msg: => String) =
+ def fail(msg: => Message) =
ok = false
def appPos = NoPosition
lazy val normalizedFun = ref(methRef)
@@ -455,12 +456,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
override def appPos = app.pos
- def fail(msg: => String, arg: Trees.Tree[T]) = {
+ def fail(msg: => Message, arg: Trees.Tree[T]) = {
ctx.error(msg, arg.pos)
ok = false
}
- def fail(msg: => String) = {
+ def fail(msg: => Message) = {
ctx.error(msg, app.pos)
ok = false
}
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index b02b0ad21..7899174f5 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -45,14 +45,14 @@ object Checking {
for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate))
ctx.error(
ex"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}",
- arg.pos)
+ arg.pos.focus)
}
/** Check that type arguments `args` conform to corresponding bounds in `poly`
* Note: This does not check the bounds of AppliedTypeTrees. These
* are handled by method checkBounds in FirstTransform
*/
- def checkBounds(args: List[tpd.Tree], poly: GenericType)(implicit ctx: Context): Unit =
+ def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit =
checkBounds(args, poly.paramBounds, _.substParams(poly, _))
/** If type is a higher-kinded application with wildcard arguments,
@@ -63,7 +63,7 @@ object Checking {
def checkWildcardHKApply(tp: Type, pos: Position)(implicit ctx: Context): Unit = tp match {
case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) =>
tycon match {
- case tycon: TypeLambda =>
+ case tycon: PolyType =>
ctx.errorOrMigrationWarning(
ex"unreducible application of higher-kinded type $tycon to wildcard arguments",
pos)
@@ -98,9 +98,9 @@ object Checking {
checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos)
checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply))
case Select(qual, name) if name.isTypeName =>
- checkRealizable(qual.tpe, qual.pos)
+ checkRealizable(qual.tpe, qual.pos.focus)
case SingletonTypeTree(ref) =>
- checkRealizable(ref.tpe, ref.pos)
+ checkRealizable(ref.tpe, ref.pos.focus)
case _ =>
}
traverseChildren(tree)
@@ -378,7 +378,7 @@ object Checking {
if (tp.symbol.is(Private) &&
!accessBoundary(sym).isContainedIn(tp.symbol.owner)) {
errors = (em"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}",
- pos) :: errors
+ sym.pos) :: errors
tp
}
else mapOver(tp)
@@ -542,6 +542,13 @@ trait Checking {
errorTree(tpt, ex"missing type parameter for ${tpt.tpe}")
}
else tpt
+
+ /** Check that `tpt` does not refer to a singleton type */
+ def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree =
+ if (tpt.tpe.isInstanceOf[SingletonType]) {
+ errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where")
+ }
+ else tpt
}
trait NoChecking extends Checking {
@@ -556,4 +563,5 @@ trait NoChecking extends Checking {
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = ()
override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
+ override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt
}
diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala
index ad84ff583..1d22dc646 100644
--- a/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -8,19 +8,20 @@ import Trees._
import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._
import Applications._, Implicits._, Flags._
import util.Positions._
-import reporting.Diagnostic
import printing.{Showable, RefinedPrinter}
import scala.collection.mutable
import java.util.regex.Matcher.quoteReplacement
+import reporting.diagnostic.Message
+import reporting.diagnostic.messages._
object ErrorReporting {
import tpd._
- def errorTree(tree: untpd.Tree, msg: => String)(implicit ctx: Context): tpd.Tree =
+ def errorTree(tree: untpd.Tree, msg: => Message)(implicit ctx: Context): tpd.Tree =
tree withType errorType(msg, tree.pos)
- def errorType(msg: => String, pos: Position)(implicit ctx: Context): ErrorType = {
+ def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = {
ctx.error(msg, pos)
ErrorType
}
@@ -101,7 +102,7 @@ object ErrorReporting {
def patternConstrStr(tree: Tree): String = ???
def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailure = NoImplicitMatches): Tree =
- errorTree(tree, typeMismatchStr(normalize(tree.tpe, pt), pt) + implicitFailure.postscript)
+ errorTree(tree, typeMismatchMsg(normalize(tree.tpe, pt), pt, implicitFailure.postscript))
/** A subtype log explaining why `found` does not conform to `expected` */
def whyNoMatchStr(found: Type, expected: Type) =
@@ -110,7 +111,7 @@ object ErrorReporting {
else
""
- def typeMismatchStr(found: Type, expected: Type) = {
+ def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = {
// replace constrained polyparams and their typevars by their bounds where possible
object reported extends TypeMap {
def setVariance(v: Int) = variance = v
@@ -132,9 +133,7 @@ object ErrorReporting {
val found1 = reported(found)
reported.setVariance(-1)
val expected1 = reported(expected)
- ex"""type mismatch:
- | found : $found1
- | required: $expected1""" + whyNoMatchStr(found, expected)
+ TypeMismatch(found1, expected1, whyNoMatchStr(found, expected), postScript)
}
/** Format `raw` implicitNotFound argument, replacing all
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index ef32e0ba6..f3dceea71 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -302,7 +302,7 @@ trait ImplicitRunInfo { self: RunInfo =>
case _ => arg
}
(apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg)))
- case tp: TypeLambda =>
+ case tp: PolyType =>
apply(tp.resType)
case _ =>
mapOver(tp)
@@ -343,7 +343,9 @@ trait ImplicitRunInfo { self: RunInfo =>
}
tp.classSymbols(liftingCtx) foreach addClassScope
case _ =>
- for (part <- tp.namedPartsWith(_.isType))
+ // We exclude lower bounds to conform to SLS 7.2:
+ // "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound"
+ for (part <- tp.namedPartsWith(_.isType, excludeLowerBounds = true))
comps ++= iscopeRefs(part)
}
comps
@@ -412,7 +414,7 @@ trait Implicits { self: Typer =>
&& !to.isError
&& !ctx.isAfterTyper
&& (ctx.mode is Mode.ImplicitsEnabled)
- && from.isInstanceOf[ValueType]
+ && from.isValueType
&& ( from.isValueSubType(to)
|| inferView(dummyTreeOfType(from), to)
(ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState)
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 4f4278468..00e92cbfb 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -898,7 +898,7 @@ class Namer { typer: Typer =>
// definition is inline (i.e. final in Scala2).
def widenRhs(tp: Type): Type = tp.widenTermRefExpr match {
case tp: ConstantType if isInline => tp
- case _ => tp.widen.approximateUnion
+ case _ => ctx.harmonizeUnion(tp.widen)
}
// Replace aliases to Unit by Unit itself. If we leave the alias in
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 0e6697fb7..08f566d49 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -43,6 +43,11 @@ object ProtoTypes {
isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
}
+ private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match {
+ case _: OrType => true
+ case pt => pt.isRef(defn.UnitClass)
+ }
+
/** Check that the result type of the current method
* fits the given expected result type.
*/
@@ -54,7 +59,7 @@ object ProtoTypes {
case _ =>
true
}
- case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) =>
+ case _: ValueTypeOrProto if !disregardProto(pt) =>
mt match {
case mt: MethodType =>
mt.isDependent || isCompatible(normalize(mt, pt), pt)
@@ -311,7 +316,7 @@ object ProtoTypes {
override def isMatchedBy(tp: Type)(implicit ctx: Context) = {
def isInstantiatable(tp: Type) = tp.widen match {
- case tp: GenericType => tp.paramNames.length == targs.length
+ case tp: PolyType => tp.paramNames.length == targs.length
case _ => false
}
isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info))
@@ -358,7 +363,7 @@ object ProtoTypes {
yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner)
val added =
- if (state.constraint contains pt) pt.duplicate(pt.paramNames, pt.paramBounds, pt.resultType)
+ if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType)
else pt
val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added)
ctx.typeComparer.addToConstraint(added, tvars)
@@ -395,9 +400,10 @@ object ProtoTypes {
if (mt.isDependent) tp
else {
val rt = normalize(mt.resultType, pt)
- if (pt.isInstanceOf[ApplyingProto])
- mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt)
- else {
+ pt match {
+ case pt: IgnoredProto => mt
+ case pt: ApplyingProto => mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt)
+ case _ =>
val ft = defn.FunctionOf(mt.paramTypes, rt)
if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt
}
diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala
index 1f150c519..834bb37a8 100644
--- a/src/dotty/tools/dotc/typer/RefChecks.scala
+++ b/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -200,7 +200,7 @@ object RefChecks {
infoStringWithLocation(other),
infoStringWithLocation(member))
else if (ctx.settings.debug.value)
- err.typeMismatchStr(memberTp, otherTp)
+ err.typeMismatchMsg(memberTp, otherTp)
else ""
"overriding %s;\n %s %s%s".format(
@@ -487,7 +487,7 @@ object RefChecks {
// abstract method, and a cursory examination of the difference reveals
// something obvious to us, let's make it more obvious to them.
val abstractParams = underlying.info.firstParamTypes
- val matchingName = clazz.info.member(underlying.name).alternatives
+ val matchingName = clazz.info.nonPrivateMember(underlying.name).alternatives
val matchingArity = matchingName filter { m =>
!m.symbol.is(Deferred) &&
m.info.firstParamTypes.length == abstractParams.length
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 0c55d977e..861847b11 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -12,6 +12,8 @@ import config.Printers.typr
import ast.Trees._
import NameOps._
import collection.mutable
+import reporting.diagnostic.Message
+import reporting.diagnostic.messages._
trait TypeAssigner {
import tpd._
@@ -220,7 +222,7 @@ trait TypeAssigner {
else ""
ctx.error(
if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor"
- else ex"$kind $name is not a member of $site$addendum",
+ else NotAMember(site, name, kind),
pos)
}
ErrorType
@@ -414,7 +416,7 @@ trait TypeAssigner {
def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = {
val ownType = tree match {
- case tree: JavaSeqLiteral => defn.ArrayOf(elemtpt.tpe)
+ case tree: untpd.JavaSeqLiteral => defn.ArrayOf(elemtpt.tpe)
case _ => if (ctx.erasedTypes) defn.SeqType else defn.SeqType.appliedTo(elemtpt.tpe)
}
tree.withType(ownType)
@@ -453,8 +455,8 @@ trait TypeAssigner {
tree.withType(ownType)
}
- def assignType(tree: untpd.TypeLambdaTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) =
- tree.withType(TypeLambda.fromSymbols(tparamDefs.map(_.symbol), body.tpe))
+ def assignType(tree: untpd.PolyTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) =
+ tree.withType(PolyType.fromSymbols(tparamDefs.map(_.symbol), body.tpe))
def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) =
tree.withType(ExprType(result.tpe))
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 3e3bb32f5..6fb0dd7c7 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -37,6 +37,7 @@ import rewrite.Rewrites.patch
import NavigateAST._
import transform.SymUtils._
import language.implicitConversions
+import printing.SyntaxHighlighting._
object Typer {
@@ -64,6 +65,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
import tpd.{cpy => _, _}
import untpd.cpy
import Dynamic.isDynamicMethod
+ import reporting.diagnostic.Message
+ import reporting.diagnostic.messages._
/** A temporary data item valid for a single typed ident:
* The set of all root import symbols that have been
@@ -96,7 +99,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
/** Method is necessary because error messages need to bind to
* to typedIdent's context which is lost in nested calls to findRef
*/
- def error(msg: => String, pos: Position) = ctx.error(msg, pos)
+ def error(msg: => Message, pos: Position) = ctx.error(msg, pos)
/** Is this import a root import that has been shadowed by an explicit
* import in the same program?
@@ -141,9 +144,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
* imported by <tree>
* or defined in <symbol>
*/
- def bindingString(prec: Int, whereFound: Context, qualifier: String = "")(implicit ctx: Context) =
- if (prec == wildImport || prec == namedImport) ex"imported$qualifier by ${whereFound.importInfo}"
- else ex"defined$qualifier in ${whereFound.owner}"
+ def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
+ if (prec == wildImport || prec == namedImport) {
+ ex"""imported$qualifier by ${hl"${whereFound.importInfo.toString}"}"""
+ } else
+ ex"""defined$qualifier in ${hl"${whereFound.owner.toString}"}"""
/** Check that any previously found result from an inner context
* does properly shadow the new one from an outer context.
@@ -166,9 +171,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
else {
if (!scala2pkg && !previous.isError && !found.isError) {
error(
- ex"""reference to $name is ambiguous;
- |it is both ${bindingString(newPrec, ctx, "")}
- |and ${bindingString(prevPrec, prevCtx, " subsequently")}""",
+ ex"""|reference to `$name` is ambiguous
+ |it is both ${bindingString(newPrec, ctx, "")}
+ |and ${bindingString(prevPrec, prevCtx, " subsequently")}""",
tree.pos)
}
previous
@@ -181,7 +186,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def checkUnambiguous(found: Type) = {
val other = namedImportRef(site, selectors.tail)
if (other.exists && found.exists && (found != other))
- error(em"reference to $name is ambiguous; it is imported twice in ${ctx.tree}",
+ error(em"reference to `$name` is ambiguous; it is imported twice in ${ctx.tree}",
tree.pos)
found
}
@@ -326,7 +331,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
if (rawType.exists)
ensureAccessible(rawType, superAccess = false, tree.pos)
else {
- error(em"not found: $kind$name", tree.pos)
+ error(new MissingIdent(tree, kind, name.show), tree.pos)
ErrorType
}
@@ -572,16 +577,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
def escapingRefs(block: Tree, localSyms: => List[Symbol])(implicit ctx: Context): collection.Set[NamedType] = {
- var hoisted: Set[Symbol] = Set()
lazy val locals = localSyms.toSet
- def leakingTypes(tp: Type): collection.Set[NamedType] =
- tp namedPartsWith (tp => locals.contains(tp.symbol))
- def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty
- def classLeaks(sym: ClassSymbol): Boolean =
- (ctx.owner is Method) || // can't hoist classes out of method bodies
- (sym.info.parents exists typeLeaks) ||
- (sym.info.decls.toList exists (t => typeLeaks(t.info)))
- leakingTypes(block.tpe)
+ block.tpe namedPartsWith (tp => locals.contains(tp.symbol))
}
/** Check that expression's type can be expressed without references to locally defined
@@ -767,10 +764,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
TypeTree(pt)
case _ =>
if (!mt.isDependent) EmptyTree
- else throw new Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error?
+ else throw new java.lang.Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error?
}
case tp =>
- throw new Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")
+ throw new java.lang.Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")
}
else typed(tree.tpt)
//println(i"typing closure $tree : ${meth1.tpe.widen}")
@@ -839,11 +836,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
mapOver(t)
}
}
- override def transform(tree: Tree)(implicit ctx: Context) =
- super.transform(tree.withType(elimWildcardSym(tree.tpe))) match {
+ override def transform(trt: Tree)(implicit ctx: Context) =
+ super.transform(trt.withType(elimWildcardSym(trt.tpe))) match {
case b: Bind =>
if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
- else ctx.error(em"duplicate pattern variable: ${b.name}", b.pos)
+ else ctx.error(new DuplicateBind(b, tree), b.pos)
b.symbol.info = elimWildcardSym(b.symbol.info)
b
case t => t
@@ -994,8 +991,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") {
- val left1 = typed(tree.left)
- val right1 = typed(tree.right)
+ val where = "in a union type"
+ val left1 = checkNotSingleton(typed(tree.left), where)
+ val right1 = checkNotSingleton(typed(tree.right), where)
assignType(cpy.OrTypeTree(tree)(left1, right1), left1, right1)
}
@@ -1055,12 +1053,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
- def typedTypeLambdaTree(tree: untpd.TypeLambdaTree)(implicit ctx: Context): Tree = track("typedTypeLambdaTree") {
- val TypeLambdaTree(tparams, body) = tree
+ def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") {
+ val PolyTypeTree(tparams, body) = tree
index(tparams)
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
val body1 = typedType(tree.body)
- assignType(cpy.TypeLambdaTree(tree)(tparams1, body1), tparams1, body1)
+ assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1)
}
def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") {
@@ -1254,7 +1252,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
.withType(dummy.nonMemberTermRef)
checkVariance(impl1)
- if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.pos)
+ if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos)
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls)
if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))
@@ -1463,7 +1461,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case tree: untpd.OrTypeTree => typedOrTypeTree(tree)
case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree)
case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree)
- case tree: untpd.TypeLambdaTree => typedTypeLambdaTree(tree)(localContext(tree, NoSymbol).setNewScope)
+ case tree: untpd.PolyTypeTree => typedPolyTypeTree(tree)(localContext(tree, NoSymbol).setNewScope)
case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree)
case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree)
case tree: untpd.Alternative => typedAlternative(tree, pt)
@@ -1915,7 +1913,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case _ =>
adaptOverloaded(ref)
}
- case poly: PolyType =>
+ case poly: PolyType if !(ctx.mode is Mode.Type) =>
if (pt.isInstanceOf[PolyProto]) tree
else {
var typeArgs = tree match {
diff --git a/src/dotty/tools/dotc/typer/Variances.scala b/src/dotty/tools/dotc/typer/Variances.scala
index a8abe5e30..92bd9fd74 100644
--- a/src/dotty/tools/dotc/typer/Variances.scala
+++ b/src/dotty/tools/dotc/typer/Variances.scala
@@ -94,7 +94,7 @@ object Variances {
v
}
varianceInArgs(varianceInType(tycon)(tparam), args, tycon.typeParams)
- case tp: GenericType =>
+ case tp: PolyType =>
flip(varianceInTypes(tp.paramBounds)(tparam)) & varianceInType(tp.resultType)(tparam)
case AnnotatedType(tp, annot) =>
varianceInType(tp)(tparam) & varianceInAnnot(annot)(tparam)
diff --git a/src/dotty/tools/dotc/util/DiffUtil.scala b/src/dotty/tools/dotc/util/DiffUtil.scala
index b7c77ad62..b55aee719 100644
--- a/src/dotty/tools/dotc/util/DiffUtil.scala
+++ b/src/dotty/tools/dotc/util/DiffUtil.scala
@@ -12,9 +12,7 @@ object DiffUtil {
private final val DELETION_COLOR = ANSI_RED
private final val ADDITION_COLOR = ANSI_GREEN
- def mkColoredCodeDiff(code: String, lastCode: String, printDiffDel: Boolean): String = {
-
- @tailrec def splitTokens(str: String, acc: List[String] = Nil): List[String] = {
+ @tailrec private def splitTokens(str: String, acc: List[String] = Nil): List[String] = {
if (str == "") {
acc.reverse
} else {
@@ -33,6 +31,35 @@ object DiffUtil {
}
}
+
+ /** @return a tuple of the (found, expected, changedPercentage) diffs as strings */
+ def mkColoredTypeDiff(found: String, expected: String): (String, String, Double) = {
+ var totalChange = 0
+ val foundTokens = splitTokens(found, Nil).toArray
+ val expectedTokens = splitTokens(expected, Nil).toArray
+
+ val diffExp = hirschberg(foundTokens, expectedTokens)
+ val diffAct = hirschberg(expectedTokens, foundTokens)
+
+ val exp = diffExp.collect {
+ case Unmodified(str) => str
+ case Inserted(str) =>
+ totalChange += str.length
+ ADDITION_COLOR + str + ANSI_DEFAULT
+ }.mkString
+
+ val fnd = diffAct.collect {
+ case Unmodified(str) => str
+ case Inserted(str) =>
+ totalChange += str.length
+ DELETION_COLOR + str + ANSI_DEFAULT
+ }.mkString
+
+ (fnd, exp, totalChange.toDouble / (expected.length + found.length))
+ }
+
+ def mkColoredCodeDiff(code: String, lastCode: String, printDiffDel: Boolean): String = {
+
val tokens = splitTokens(code, Nil).toArray
val lastTokens = splitTokens(lastCode, Nil).toArray
diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala
index 8bd0ecfd6..1d4c9c2ab 100644
--- a/src/dotty/tools/dotc/util/SourceFile.scala
+++ b/src/dotty/tools/dotc/util/SourceFile.scala
@@ -97,7 +97,7 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) extends interfac
private lazy val lineIndices: Array[Int] = calculateLineIndices(content)
/** Map line to offset of first character in line */
- def lineToOffset(index : Int): Int = lineIndices(index)
+ def lineToOffset(index: Int): Int = lineIndices(index)
/** A cache to speed up offsetToLine searches to similar lines */
private var lastLine = 0
diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala
index 68a9b6403..595ea34ca 100644
--- a/src/dotty/tools/dotc/util/SourcePosition.scala
+++ b/src/dotty/tools/dotc/util/SourcePosition.scala
@@ -14,6 +14,23 @@ extends interfaces.SourcePosition {
def point: Int = pos.point
/** The line of the position, starting at 0 */
def line: Int = source.offsetToLine(point)
+
+ /** The lines of the position */
+ def lines: List[Int] =
+ List.range(source.offsetToLine(start), source.offsetToLine(end + 1)) match {
+ case Nil => line :: Nil
+ case xs => xs
+ }
+
+ def lineOffsets: List[Int] =
+ lines.map(source.lineToOffset(_))
+
+ def lineContent(lineNumber: Int): String =
+ source.lineContent(source.lineToOffset(lineNumber))
+
+ def beforeAndAfterPoint: (List[Int], List[Int]) =
+ lineOffsets.partition(_ < point)
+
/** The column of the position, starting at 0 */
def column: Int = source.column(point)
diff --git a/src/strawman/collections/CollectionStrawMan4.scala b/src/strawman/collections/CollectionStrawMan4.scala
index fb95ea59f..7e8de2c82 100644
--- a/src/strawman/collections/CollectionStrawMan4.scala
+++ b/src/strawman/collections/CollectionStrawMan4.scala
@@ -216,7 +216,7 @@ object CollectionStrawMan4 {
object ListBuffer extends IterableFactory[ListBuffer] {
def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = coll match {
- case pd @ View.Partitioned(partition: View.Partition[B]) =>
+ case pd @ View.Partitioned(partition: View.Partition[B] @unchecked) =>
partition.distribute(new ListBuffer[B]())
new ListBuffer[B] ++= pd.forced.get
case _ =>
@@ -267,7 +267,7 @@ object CollectionStrawMan4 {
Array.copy(fst.elems, fst.start, elems, 0, fst.length)
Array.copy(snd.elems, snd.start, elems, fst.length, snd.length)
new ArrayBuffer(elems, elems.length)
- case pd @ View.Partitioned(partition: View.Partition[B]) =>
+ case pd @ View.Partitioned(partition: View.Partition[B] @unchecked) =>
partition.distribute(new ArrayBuffer[B]())
pd.forced.get.asInstanceOf[ArrayBuffer[B]]
case c if c.knownLength >= 0 =>