aboutsummaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-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.scala1
-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/TypeComparer.scala33
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala5
-rw-r--r--compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala25
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala47
11 files changed, 100 insertions, 63 deletions
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 1be47c1da..4d4350f98 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -355,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/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/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 cb462af45..546473b68 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -1681,7 +1681,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.
*/
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/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/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
}