aboutsummaryrefslogtreecommitdiff
path: root/compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src')
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeInfo.scala27
-rw-r--r--compiler/src/dotty/tools/dotc/ast/untpd.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/config/Config.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/core/Constraint.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala24
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala5
-rw-r--r--compiler/src/dotty/tools/dotc/core/Flags.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala8
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/SymDenotations.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/TypeComparer.scala33
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala23
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java4
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala47
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala25
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala12
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala28
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Namer.scala130
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala47
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala1
22 files changed, 308 insertions, 140 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index dd890dc50..b00d7df71 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -26,23 +26,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => false
}
- /** The largest subset of {NoInits, PureInterface} that a
- * trait enclosing this statement can have as flags.
- * Does tree contain an initialization part when seen as a member of a class or trait?
- */
- def defKind(tree: Tree): FlagSet = unsplice(tree) match {
- case EmptyTree | _: Import =>
- NoInitsInterface
- case tree: TypeDef =>
- if (tree.isClassDef) NoInits else NoInitsInterface
- case tree: DefDef =>
- if (tree.unforcedRhs == EmptyTree && tree.vparamss.forall(_.forall(_.unforcedRhs == EmptyTree))) NoInitsInterface else NoInits
- case tree: ValDef =>
- if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
- case _ =>
- EmptyFlags
- }
-
def isOpAssign(tree: Tree) = unsplice(tree) match {
case Apply(fn, _ :: _) =>
unsplice(fn) match {
@@ -588,6 +571,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
accum(Nil, root)
}
+ /** The largest subset of {NoInits, PureInterface} that a
+ * trait enclosing this statement can have as flags.
+ */
+ def defKind(tree: Tree): FlagSet = unsplice(tree) match {
+ case EmptyTree | _: Import => NoInitsInterface
+ case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
+ case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits
+ case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
+ case _ => EmptyFlags
+ }
/** The top level classes in this tree, including only those module classes that
* are not a linked class of some other class in the result.
diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala
index 99bc3240f..8ca91590f 100644
--- a/compiler/src/dotty/tools/dotc/ast/untpd.scala
+++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala
@@ -149,6 +149,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def is(fs: FlagSet): Boolean = flags is fs
def is(fc: FlagConjunction): Boolean = flags is fc
+ def is(fc: FlagSet, butNot: FlagSet): Boolean = flags.is(fc, butNot = butNot)
def | (fs: FlagSet): Modifiers = withFlags(flags | fs)
def & (fs: FlagSet): Modifiers = withFlags(flags & fs)
diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala
index 119af9483..dc56ad8b8 100644
--- a/compiler/src/dotty/tools/dotc/config/Config.scala
+++ b/compiler/src/dotty/tools/dotc/config/Config.scala
@@ -75,6 +75,15 @@ object Config {
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
final val fastPathForRefinedSubtype = true
+ /** If this flag is set, and we compute `T1 { X = S1 }` & `T2 { X = S2 }` as a new
+ * upper bound of a constrained parameter, try to align the refinements by computing
+ * `S1 =:= S2` (which might instantiate type parameters).
+ * This rule is contentious because it cuts the constraint set.
+ *
+ * For more info, see the comment in `TypeComparer#distributeAnd`.
+ */
+ final val alignArgsInAnd = true
+
/** If this flag is set, higher-kinded applications are checked for validity
*/
final val checkHKApplications = false
diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala
index c99b748b7..50136a26c 100644
--- a/compiler/src/dotty/tools/dotc/core/Constraint.scala
+++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala
@@ -111,12 +111,6 @@ abstract class Constraint extends Showable {
*/
def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This
- /** Narrow one of the bounds of type parameter `param`
- * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
- * that `param >: bound`.
- */
- def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This
-
/** Is entry associated with `pt` removable? This is the case if
* all type parameters of the entry are associated with type variables
* which have their `inst` fields set.
diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
index b3c50fb71..2a1f4ee6e 100644
--- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
+++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
@@ -44,6 +44,13 @@ trait ConstraintHandling {
try op finally alwaysFluid = saved
}
+ /** If set, align arguments `S1`, `S2`when taking the glb
+ * `T1 { X = S1 } & T2 { X = S2 }` of a constraint upper bound for some type parameter.
+ * Aligning means computing `S1 =:= S2` which may change the current constraint.
+ * See note in TypeComparer#distributeAnd.
+ */
+ protected var homogenizeArgs = false
+
/** We are currently comparing polytypes. Used as a flag for
* optimization: when `false`, no need to do an expensive `pruneLambdaParams`
*/
@@ -64,7 +71,8 @@ trait ConstraintHandling {
}
if (Config.checkConstraintsSeparated)
assert(!occursIn(bound), s"$param occurs in $bound")
- val c1 = constraint.narrowBound(param, bound, isUpper)
+ val newBound = narrowedBound(param, bound, isUpper)
+ val c1 = constraint.updateEntry(param, newBound)
(c1 eq constraint) || {
constraint = c1
val TypeBounds(lo, hi) = constraint.entry(param)
@@ -72,6 +80,20 @@ trait ConstraintHandling {
}
}
+ /** Narrow one of the bounds of type parameter `param`
+ * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
+ * that `param >: bound`.
+ */
+ def narrowedBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = {
+ val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
+ val saved = homogenizeArgs
+ homogenizeArgs = Config.alignArgsInAnd
+ try
+ if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
+ else oldBounds.derivedTypeBounds(lo | bound, hi)
+ finally homogenizeArgs = saved
+ }
+
protected def addUpperBound(param: PolyParam, bound: Type): Boolean = {
def description = i"constraint $param <: $bound to\n$constraint"
if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) {
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 3cab75f93..4d4350f98 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -299,6 +299,8 @@ class Definitions {
lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef")
def ScalaPredefModule(implicit ctx: Context) = ScalaPredefModuleRef.symbol
+ lazy val Predef_ConformsR = ScalaPredefModule.requiredClass("$less$colon$less").typeRef
+ def Predef_Conforms(implicit ctx: Context) = Predef_ConformsR.symbol
lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms")
def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol
lazy val Predef_classOfR = ScalaPredefModule.requiredMethodRef("classOf")
@@ -336,6 +338,8 @@ class Definitions {
def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol
def Predef_eqAny(implicit ctx: Context) = DottyPredefModule.requiredMethod(nme.eqAny)
+ lazy val Predef_ImplicitConverterR = DottyPredefModule.requiredClass("ImplicitConverter").typeRef
+ def Predef_ImplicitConverter(implicit ctx: Context) = Predef_ImplicitConverterR.symbol
lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol
@@ -351,6 +355,7 @@ class Definitions {
enterCompleteClassSymbol(
ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final,
List(AnyClass.typeRef), EmptyScope)
+ def SingletonType = SingletonClass.typeRef
lazy val SeqType: TypeRef = ctx.requiredClassRef("scala.collection.Seq")
def SeqClass(implicit ctx: Context) = SeqType.symbol.asClass
diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala
index d2a1c58f5..c1267d8a2 100644
--- a/compiler/src/dotty/tools/dotc/core/Flags.scala
+++ b/compiler/src/dotty/tools/dotc/core/Flags.scala
@@ -287,9 +287,10 @@ object Flags {
/** A trait that has only abstract methods as members
- * (and therefore can be represented by a Java interface
+ * and therefore can be represented by a Java interface.
+ * Warning: flag is set during regular typer pass, should be tested only after typer.
*/
- final val PureInterface = typeFlag(22, "interface") // TODO when unpickling, reconstitute from context
+ final val PureInterface = typeFlag(22, "interface")
/** Labeled with of abstract & override */
final val AbsOverride = termFlag(22, "abstract override")
@@ -338,7 +339,9 @@ object Flags {
final val JavaStaticTerm = JavaStatic.toTermFlags
final val JavaStaticType = JavaStatic.toTypeFlags
- /** Trait does not have fields or initialization code */
+ /** Trait does not have fields or initialization code.
+ * Warning: flag is set during regular typer pass, should be tested only after typer.
+ */
final val NoInits = typeFlag(32, "<noInits>")
/** Variable is accessed from nested function. */
diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
index 72c7a8e51..61dd5a445 100644
--- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
+++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
@@ -354,14 +354,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
updateEntry(p1, p1Bounds).replace(p2, p1)
}
- def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This = {
- val oldBounds @ TypeBounds(lo, hi) = nonParamBounds(param)
- val newBounds =
- if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
- else oldBounds.derivedTypeBounds(lo | bound, hi)
- updateEntry(param, newBounds)
- }
-
// ---------- Removals ------------------------------------------------------------
/** A new constraint which is derived from this constraint by removing
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index 766e3733f..5b7dc3d1d 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -97,6 +97,7 @@ object StdNames {
val EMPTY: N = ""
val EMPTY_PACKAGE: N = Names.EMPTY_PACKAGE.toString
val EVIDENCE_PARAM_PREFIX: N = "evidence$"
+ val DEP_PARAM_PREFIX = "<param>"
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
val EXPAND_SEPARATOR: N = "$$"
val IMPL_CLASS_SUFFIX: N = "$class"
diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
index 9b9caf8e7..c98b444d9 100644
--- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -158,7 +158,7 @@ object SymDenotations {
final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags }
/** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */
- final def setApplicableFlags(flags: FlagSet): Unit = {
+ final def setNoInitsFlags(flags: FlagSet): Unit = {
val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits
setFlag(flags & mask)
}
diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
index b97dfe684..b61fccf31 100644
--- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -1300,23 +1300,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case tp1: RefinedType =>
tp2 match {
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
- // Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }`, if `S1 =:= S2`
- // (possibly by instantiating type parameters), rewrite to `T1 & T2 { X = S1 }`.
- // Otherwise rewrite to `T1 & T2 { X B }` where `B` is the conjunction of
- // the bounds of `X` in `T1` and `T2`.
- // The first rule above is contentious because it cuts the constraint set.
- // But without it we would replace the two aliases by
- // `T { X >: S1 | S2 <: S1 & S2 }`, which looks weird and is probably
- // not what's intended.
+ // Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }` rewrite to
+ // `T1 & T2 { X B }` where `B` is the conjunction of the bounds of `X` in `T1` and `T2`.
+ //
+ // However, if `homogenizeArgs` is set, and both aliases `X = Si` are
+ // nonvariant, and `S1 =:= S2` (possibly by instantiating type parameters),
+ // rewrite instead to `T1 & T2 { X = S1 }`. This rule is contentious because
+ // it cuts the constraint set. On the other hand, without it we would replace
+ // the two aliases by `T { X >: S1 | S2 <: S1 & S2 }`, which looks weird
+ // and is probably not what's intended.
val rinfo1 = tp1.refinedInfo
val rinfo2 = tp2.refinedInfo
val parent = tp1.parent & tp2.parent
- val rinfo =
- if (rinfo1.isAlias && rinfo2.isAlias && isSameType(rinfo1, rinfo2))
- rinfo1
- else
- rinfo1 & rinfo2
- tp1.derivedRefinedType(parent, tp1.refinedName, rinfo)
+
+ def isNonvariantAlias(tp: Type) = tp match {
+ case tp: TypeAlias => tp.variance == 0
+ case _ => false
+ }
+ if (homogenizeArgs &&
+ isNonvariantAlias(rinfo1) && isNonvariantAlias(rinfo2))
+ isSameType(rinfo1, rinfo2) // establish new constraint
+
+ tp1.derivedRefinedType(parent, tp1.refinedName, rinfo1 & rinfo2)
case _ =>
NoType
}
diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
index 9dd767a8e..b8f81f1bb 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -19,6 +19,7 @@ import util.Positions.{Position, NoPosition}
import util.Stats._
import util.{DotClass, SimpleMap}
import reporting.diagnostic.Message
+import reporting.diagnostic.messages.CyclicReferenceInvolving
import ast.tpd._
import ast.TreeTypeMap
import printing.Texts._
@@ -1273,10 +1274,14 @@ object Types {
def underlying(implicit ctx: Context): Type
/** The closest supertype of this type. This is the same as `underlying`,
- * except for TypeRefs where the upper bound is returned, and HKApplys,
- * where the upper bound of the constructor is re-applied to the arguments.
+ * except that
+ * - instead of a TyperBounds type it returns its upper bound, and
+ * - for HKApplys it returns the upper bound of the constructor re-applied to the arguments.
*/
- def superType(implicit ctx: Context): Type = underlying
+ def superType(implicit ctx: Context): Type = underlying match {
+ case TypeBounds(_, hi) => hi
+ case st => st
+ }
}
// Every type has to inherit one of the following four abstract type classes.,
@@ -1680,7 +1685,10 @@ object Types {
}
else newLikeThis(prefix)
}
- else newLikeThis(prefix)
+ else prefix match {
+ case _: WildcardType => WildcardType
+ case _ => newLikeThis(prefix)
+ }
/** Create a NamedType of the same kind as this type, but with a new prefix.
*/
@@ -1762,11 +1770,6 @@ object Types {
type ThisType = TypeRef
override def underlying(implicit ctx: Context): Type = info
-
- override def superType(implicit ctx: Context): Type = info match {
- case TypeBounds(_, hi) => hi
- case _ => info
- }
}
final class TermRefWithSignature(prefix: Type, name: TermName, override val sig: Signature) extends TermRef(prefix, name) {
@@ -3853,7 +3856,7 @@ object Types {
class CyclicReference private (val denot: SymDenotation)
extends TypeError(s"cyclic reference involving $denot") {
- def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}"
+ def toMessage(implicit ctx: Context) = CyclicReferenceInvolving(denot)
}
object CyclicReference {
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index fcba957c0..fdb8a97ae 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -768,7 +768,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
}
else EmptyValDef
setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe)
- cls.setApplicableFlags(fork.indexStats(end))
+ cls.setNoInitsFlags(fork.indexStats(end))
val constr = readIndexedDef().asInstanceOf[DefDef]
def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) =
diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
index 0d1068b8c..6af902d1b 100644
--- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
+++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
@@ -4,6 +4,7 @@ package printing
import core._
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, Denotations._
import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation
+import TypeApplications.AppliedType
import StdNames.{nme, tpnme}
import ast.Trees._, ast._
import typer.Implicits._
@@ -119,10 +120,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
}
/** The longest sequence of refinement types, starting at given type
- * and following parents.
+ * and following parents, but stopping at applied types.
*/
private def refinementChain(tp: Type): List[Type] =
tp :: (tp match {
+ case AppliedType(_, _) => Nil
case tp: RefinedType => refinementChain(tp.parent.stripTypeVar)
case _ => Nil
})
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
index a530a937f..7b56c8ed4 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
@@ -51,6 +51,10 @@ public enum ErrorMessageID {
MixedLeftAndRightAssociativeOpsID,
CantInstantiateAbstractClassOrTraitID,
AnnotatedPrimaryConstructorRequiresModifierOrThisID,
+ OverloadedOrRecursiveMethodNeedsResultTypeID,
+ RecursiveValueNeedsResultTypeID,
+ CyclicReferenceInvolvingID,
+ CyclicReferenceInvolvingImplicitID,
;
public int errorNumber() {
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 54090074a..4c53fa496 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -18,6 +18,7 @@ import dotc.parsing.Tokens
import printing.Highlighting._
import printing.Formatting
import ErrorMessageID._
+import dotty.tools.dotc.core.SymDenotations.SymDenotation
object messages {
@@ -1134,7 +1135,7 @@ object messages {
}
case class AnnotatedPrimaryConstructorRequiresModifierOrThis(cls: Name)(implicit ctx: Context)
- extends Message(AnnotatedPrimaryConstructorRequiresModifierOrThisID) {
+ extends Message(AnnotatedPrimaryConstructorRequiresModifierOrThisID) {
val kind = "Syntax"
val msg = hl"""${"private"}, ${"protected"}, or ${"this"} expected for annotated primary constructor"""
val explanation =
@@ -1147,4 +1148,48 @@ object messages {
| ^^^^
|""".stripMargin
}
+
+ case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
+ extends Message(OverloadedOrRecursiveMethodNeedsResultTypeID) {
+ val kind = "Syntax"
+ val msg = hl"""overloaded or recursive method ${tree} needs return type"""
+ val explanation =
+ hl"""Case 1: ${tree} is overloaded
+ |If there are multiple methods named `${tree.name}` and at least one definition of
+ |it calls another, you need to specify the calling method's return type.
+ |
+ |Case 2: ${tree} is recursive
+ |If `${tree.name}` calls itself on any path, you need to specify its return type.
+ |""".stripMargin
+ }
+
+ case class RecursiveValueNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
+ extends Message(RecursiveValueNeedsResultTypeID) {
+ val kind = "Syntax"
+ val msg = hl"""recursive value ${tree.name} needs type"""
+ val explanation =
+ hl"""The definition of `${tree.name}` is recursive and you need to specify its type.
+ |""".stripMargin
+ }
+
+ case class CyclicReferenceInvolving(denot: SymDenotation)(implicit ctx: Context)
+ extends Message(CyclicReferenceInvolvingID) {
+ val kind = "Syntax"
+ val msg = hl"""cyclic reference involving $denot"""
+ val explanation =
+ hl"""|$denot is declared as part of a cycle which makes it impossible for the
+ |compiler to decide upon ${denot.name}'s type.
+ |""".stripMargin
+ }
+
+ case class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(implicit ctx: Context)
+ extends Message(CyclicReferenceInvolvingImplicitID) {
+ val kind = "Syntax"
+ val msg = hl"""cyclic reference involving implicit $cycleSym"""
+ val explanation =
+ hl"""|This happens when the right hand-side of $cycleSym's definition involves an implicit search.
+ |To avoid this error, give `${cycleSym.name}` an explicit type.
+ |""".stripMargin
+ }
+
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala
index 222717e7e..a65ef00cc 100644
--- a/compiler/src/dotty/tools/dotc/typer/Applications.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala
@@ -213,16 +213,15 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
protected def init() = methType match {
case methType: MethodType =>
// apply the result type constraint, unless method type is dependent
- if (!methType.isDependent) {
- val savedConstraint = ctx.typerState.constraint
- if (!constrainResult(methType.resultType, resultType))
- if (ctx.typerState.isCommittable)
- // defer the problem until after the application;
- // it might be healed by an implicit conversion
- assert(ctx.typerState.constraint eq savedConstraint)
- else
- fail(err.typeMismatchMsg(methType.resultType, resultType))
- }
+ val resultApprox = resultTypeApprox(methType)
+ val savedConstraint = ctx.typerState.constraint
+ if (!constrainResult(resultApprox, resultType))
+ if (ctx.typerState.isCommittable)
+ // defer the problem until after the application;
+ // it might be healed by an implicit conversion
+ assert(ctx.typerState.constraint eq savedConstraint)
+ else
+ fail(err.typeMismatchMsg(methType.resultType, resultType))
// match all arguments with corresponding formal parameters
matchArgs(orderedArgs, methType.paramTypes, 0)
case _ =>
@@ -1100,10 +1099,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
/** Drop any implicit parameter section */
def stripImplicit(tp: Type): Type = tp match {
- case mt: ImplicitMethodType if !mt.isDependent =>
- mt.resultType
- // todo: make sure implicit method types are not dependent?
- // but check test case in /tests/pos/depmet_implicit_chaining_zw.scala
+ case mt: ImplicitMethodType =>
+ resultTypeApprox(mt)
case pt: PolyType =>
pt.derivedPolyType(pt.paramNames, pt.paramBounds, stripImplicit(pt.resultType))
case _ =>
diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
index 0978c2c1e..a1690955f 100644
--- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -28,24 +28,22 @@ object ErrorReporting {
def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
val cycleSym = ex.denot.symbol
- def errorMsg(msg: String, cx: Context): String =
+ def errorMsg(msg: Message, cx: Context): Message =
if (cx.mode is Mode.InferringReturnType) {
cx.tree match {
case tree: untpd.DefDef if !tree.tpt.typeOpt.exists =>
- em"overloaded or recursive method ${tree.name} needs result type"
+ OverloadedOrRecursiveMethodNeedsResultType(tree.name)
case tree: untpd.ValDef if !tree.tpt.typeOpt.exists =>
- em"recursive value ${tree.name} needs type"
+ RecursiveValueNeedsResultType(tree.name)
case _ =>
errorMsg(msg, cx.outer)
}
} else msg
if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm)
- em"""cyclic reference involving implicit $cycleSym
- |This happens when the right hand-side of $cycleSym's definition involves an implicit search.
- |To avoid the error, give $cycleSym an explicit type."""
+ CyclicReferenceInvolvingImplicit(cycleSym)
else
- errorMsg(ex.show, ctx)
+ errorMsg(ex.toMessage, ctx)
}
def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) =
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
index 759cc62e9..ebbcbcc95 100644
--- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -82,11 +82,29 @@ object Implicits {
case tpw: TermRef =>
false // can't discard overloaded refs
case tpw =>
- //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType))
- // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}")
- !tpw.derivesFrom(defn.FunctionClass(1)) ||
- ref.symbol == defn.Predef_conforms //
- // as an implicit conversion, Predef.$conforms is a no-op, so exclude it
+ // Only direct instances of Function1 and direct or indirect instances of <:< are eligible as views.
+ // However, Predef.$conforms is not eligible, because it is a no-op.
+ //
+ // In principle, it would be cleanest if only implicit methods qualified
+ // as implicit conversions. We could achieve that by having standard conversions like
+ // this in Predef:
+ //
+ // implicit def convertIfConforms[A, B](x: A)(implicit ev: A <:< B): B = ev(a)
+ // implicit def convertIfConverter[A, B](x: A)(implicit ev: ImplicitConverter[A, B]): B = ev(a)
+ //
+ // (Once `<:<` inherits from `ImplicitConverter` we only need the 2nd one.)
+ // But clauses like this currently slow down implicit search a lot, because
+ // they are eligible for all pairs of types, and therefore are tried too often.
+ // We emulate instead these conversions directly in the search.
+ // The reason for leaving out `Predef_conforms` is that we know it adds
+ // nothing since it only relates subtype with supertype.
+ //
+ // We keep the old behavior under -language:Scala2.
+ val isFunctionInS2 = ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1))
+ val isImplicitConverter = tpw.derivesFrom(defn.Predef_ImplicitConverter)
+ val isConforms =
+ tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms
+ !(isFunctionInS2 || isImplicitConverter || isConforms)
}
def discardForValueType(tpw: Type): Boolean = tpw match {
diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala
index 96660f15c..f2ad1f7c9 100644
--- a/compiler/src/dotty/tools/dotc/typer/Namer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala
@@ -484,46 +484,91 @@ class Namer { typer: Typer =>
/** Create top-level symbols for statements and enter them into symbol table */
def index(stats: List[Tree])(implicit ctx: Context): Context = {
- val classDef = mutable.Map[TypeName, TypeDef]()
- val moduleDef = mutable.Map[TypeName, TypeDef]()
+ // module name -> (stat, moduleCls | moduleVal)
+ val moduleClsDef = mutable.Map[TypeName, (Tree, TypeDef)]()
+ val moduleValDef = mutable.Map[TermName, (Tree, ValDef)]()
+
+ /** Remove the subtree `tree` from the expanded tree of `mdef` */
+ def removeInExpanded(mdef: Tree, tree: Tree): Unit = {
+ val Thicket(trees) = expanded(mdef)
+ mdef.putAttachment(ExpandedTree, Thicket(trees.filter(_ != tree)))
+ }
+
+ /** Merge the module class `modCls` in the expanded tree of `mdef` with the given stats */
+ def mergeModuleClass(mdef: Tree, modCls: TypeDef, stats: List[Tree]): TypeDef = {
+ var res: TypeDef = null
+ val Thicket(trees) = expanded(mdef)
+ val merged = trees.map { tree =>
+ if (tree == modCls) {
+ val impl = modCls.rhs.asInstanceOf[Template]
+ res = cpy.TypeDef(modCls)(rhs = cpy.Template(impl)(body = stats ++ impl.body))
+ res
+ }
+ else tree
+ }
+
+ mdef.putAttachment(ExpandedTree, Thicket(merged))
+
+ res
+ }
+
+ /** Merge `fromCls` of `fromStat` into `toCls` of `toStat`
+ * if the former is synthetic and the latter not.
+ *
+ * Note:
+ * 1. `fromStat` and `toStat` could be the same stat
+ * 2. `fromCls` and `toCls` are necessarily different
+ */
+ def mergeIfSynthetic(fromStat: Tree, fromCls: TypeDef, toStat: Tree, toCls: TypeDef): Unit =
+ if (fromCls.mods.is(Synthetic) && !toCls.mods.is(Synthetic)) {
+ removeInExpanded(fromStat, fromCls)
+ val mcls = mergeModuleClass(toStat, toCls, fromCls.rhs.asInstanceOf[Template].body)
+ moduleClsDef(fromCls.name) = (toStat, mcls)
+ }
/** Merge the definitions of a synthetic companion generated by a case class
* and the real companion, if both exist.
*/
def mergeCompanionDefs() = {
- for (cdef @ TypeDef(name, _) <- stats)
- if (cdef.isClassDef) {
- classDef(name) = cdef
- cdef.attachmentOrElse(ExpandedTree, cdef) match {
- case Thicket(cls :: mval :: (mcls @ TypeDef(mname, _: Template)) :: crest)
- if name.moduleClassName == mname =>
- moduleDef(name) = mcls
- case _ =>
- }
- }
- for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) {
- val typName = name.toTypeName
- // Expansion of object is a flattened thicket with the first two elements being:
- // module val :: module class :: rest
- val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: rest) = expanded(mdef)
- moduleDef(typName) = mcls
- classDef get name.toTypeName match {
- case Some(cdef) =>
- cdef.attachmentOrElse(ExpandedTree, cdef) match {
- case Thicket(cls :: mval :: TypeDef(mname, compimpl: Template) :: crest)
- if name.moduleClassName == mname =>
- val mcls1 = cpy.TypeDef(mcls)(
- rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body))
- mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: rest))
- moduleDef(typName) = mcls1
- cdef.putAttachment(ExpandedTree, Thicket(cls :: crest))
+ def valid(mdef: MemberDef): Boolean = mdef.mods.is(Module, butNot = Package)
+
+ for (stat <- stats)
+ expanded(stat) match {
+ case Thicket(trees) => // companion object always expands to thickets
+ trees.map {
+ case mcls @ TypeDef(name, impl: Template) if valid(mcls) =>
+ moduleClsDef.get(name) match {
+ case Some((stat1, mcls1@TypeDef(_, impl1: Template))) =>
+ mergeIfSynthetic(stat, mcls, stat1, mcls1)
+ mergeIfSynthetic(stat1, mcls1, stat, mcls)
+ case None =>
+ moduleClsDef(name) = (stat, mcls)
+ }
+
+ case vdef @ ValDef(name, _, _) if valid(vdef) =>
+ moduleValDef.get(name) match {
+ case Some((stat1, vdef1)) =>
+ if (vdef.mods.is(Synthetic) && !vdef1.mods.is(Synthetic))
+ removeInExpanded(stat, vdef)
+ else if (!vdef.mods.is(Synthetic) && vdef1.mods.is(Synthetic)) {
+ removeInExpanded(stat1, vdef1)
+ moduleValDef(name) = (stat, vdef)
+ }
+ else {
+ // double definition of objects or case classes, handled elsewhere
+ }
+ case None =>
+ moduleValDef(name) = (stat, vdef)
+ }
+
case _ =>
}
- case none =>
+ case _ =>
+
}
- }
}
+ /** Create links between companion object and companion class */
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
val claz = ctx.denotNamed(classTree.name.encode).symbol
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
@@ -532,8 +577,29 @@ class Namer { typer: Typer =>
}
def createCompanionLinks(implicit ctx: Context): Unit = {
+ val classDef = mutable.Map[TypeName, TypeDef]()
+ val moduleDef = mutable.Map[TypeName, TypeDef]()
+
+ def updateCache(cdef: TypeDef): Unit = {
+ if (!cdef.isClassDef || cdef.mods.is(Package)) return
+
+ if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef
+ else classDef(cdef.name) = cdef
+ }
+
+ for (stat <- stats)
+ expanded(stat) match {
+ case cdef : TypeDef => updateCache(cdef)
+ case Thicket(trees) =>
+ trees.map {
+ case cdef: TypeDef => updateCache(cdef)
+ case _ =>
+ }
+ case _ =>
+ }
+
for (cdef @ TypeDef(name, _) <- classDef.values) {
- moduleDef.getOrElse(name, EmptyTree) match {
+ moduleDef.getOrElse(name.moduleClassName, EmptyTree) match {
case t: TypeDef =>
createLinks(cdef, t)
case EmptyTree =>
@@ -818,8 +884,6 @@ class Namer { typer: Typer =>
Checking.checkWellFormed(cls)
if (isDerivedValueClass(cls)) cls.setFlag(Final)
- cls.setApplicableFlags(
- (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
cls.info = avoidPrivateLeaks(cls, cls.pos)
}
}
diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
index d666b563e..17f13d7c1 100644
--- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -57,19 +57,14 @@ object ProtoTypes {
case pt: FunProto =>
mt match {
case mt: MethodType =>
- mt.isDependent || constrainResult(mt.resultType, pt.resultType)
+ constrainResult(resultTypeApprox(mt), pt.resultType)
case _ =>
true
}
case _: ValueTypeOrProto if !disregardProto(pt) =>
- mt match {
- case mt: MethodType =>
- mt.isDependent || isCompatible(normalize(mt, pt), pt)
- case _ =>
- isCompatible(mt, pt)
- }
- case _: WildcardType =>
- isCompatible(mt, pt)
+ isCompatible(normalize(mt, pt), pt)
+ case pt: WildcardType if pt.optBounds.exists =>
+ isCompatible(normalize(mt, pt), pt)
case _ =>
true
}
@@ -394,6 +389,26 @@ object ProtoTypes {
/** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */
def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1
+ /** Create a new polyparam that represents a dependent method parameter singleton */
+ def newDepPolyParam(tp: Type)(implicit ctx: Context): PolyParam = {
+ val poly = PolyType(ctx.freshName(nme.DEP_PARAM_PREFIX).toTypeName :: Nil, 0 :: Nil)(
+ pt => TypeBounds.upper(AndType(tp, defn.SingletonType)) :: Nil,
+ pt => defn.AnyType)
+ ctx.typeComparer.addToConstraint(poly, Nil)
+ PolyParam(poly, 0)
+ }
+
+ /** The result type of `mt`, where all references to parameters of `mt` are
+ * replaced by either wildcards (if typevarsMissContext) or polyparams.
+ */
+ def resultTypeApprox(mt: MethodType)(implicit ctx: Context): Type =
+ if (mt.isDependent) {
+ def replacement(tp: Type) =
+ if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepPolyParam(tp)
+ mt.resultType.substParams(mt, mt.paramTypes.map(replacement))
+ }
+ else mt.resultType
+
/** The normalized form of a type
* - unwraps polymorphic types, tracking their parameters in the current constraint
* - skips implicit parameters; if result type depends on implicit parameter,
@@ -413,22 +428,18 @@ object ProtoTypes {
tp.widenSingleton match {
case poly: PolyType => normalize(constrained(poly).resultType, pt)
case mt: MethodType =>
- if (mt.isImplicit)
- if (mt.isDependent)
- mt.resultType.substParams(mt, mt.paramTypes.map(Function.const(WildcardType)))
- else mt.resultType
- else
- if (mt.isDependent) tp
- else {
- val rt = normalize(mt.resultType, pt)
+ if (mt.isImplicit) resultTypeApprox(mt)
+ else if (mt.isDependent) tp
+ else {
+ val rt = normalize(mt.resultType, pt)
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
- }
}
+ }
case et: ExprType => et.resultType
case _ => tp
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index b2e9d639d..ba14b7498 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1314,6 +1314,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
val dummy = localDummy(cls, impl)
val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
+ cls.setNoInitsFlags((NoInitsInterface /: body1)((fs, stat) => fs & defKind(stat)))
// Expand comments and type usecases
cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)