aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala36
-rw-r--r--src/dotty/tools/dotc/transform/FirstTransform.scala7
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala29
4 files changed, 44 insertions, 30 deletions
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index 687ca9ef0..7b76feb4d 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -7,6 +7,8 @@ import config.Printers._
import Decorators._
import StdNames._
import util.SimpleMap
+import collection.mutable
+import ast.tpd._
trait TypeOps { this: Context =>
@@ -307,6 +309,40 @@ trait TypeOps { this: Context =>
parentRefs
}
+ /** An argument bounds violation is a triple consisting of
+ * - the argument tree
+ * - a string "upper" or "lower" indicating which bound is violated
+ * - the violated bound
+ */
+ type BoundsViolation = (Tree, String, Type)
+
+ /** The list of violations where arguments are not within bounds.
+ * @param args The arguments
+ * @param boundss The list of type bounds
+ * @param instantiate A function that maps a bound type and the list of argument types to a resulting type.
+ * Needed to handle bounds that refer to other bounds.
+ */
+ def boundsViolations(args: List[Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): List[BoundsViolation] = {
+ val argTypes = args.tpes
+ val violations = new mutable.ListBuffer[BoundsViolation]
+ for ((arg, bounds) <- args zip boundss) {
+ def checkOverlapsBounds(lo: Type, hi: Type): Unit = {
+ //println(i"instantiating ${bounds.hi} with $argTypes")
+ //println(i" = ${instantiate(bounds.hi, argTypes)}")
+ val hiBound = instantiate(bounds.hi, argTypes.mapConserve(_.bounds.hi))
+ // Note that argTypes can contain a TypeBounds type for arguments that are
+ // not fully determined. In that case we need to check against the hi bound of the argument.
+ if (!(lo <:< hiBound)) violations += ((arg, "upper", hiBound))
+ if (!(bounds.lo <:< hi)) violations += ((arg, "lower", bounds.lo))
+ }
+ arg.tpe match {
+ case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi)
+ case tp => checkOverlapsBounds(tp, tp)
+ }
+ }
+ violations.toList
+ }
+
/** Is `feature` enabled in class `owner`?
* This is the case if one of the following two alternatives holds:
*
diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala
index a58e8a643..42ace148a 100644
--- a/src/dotty/tools/dotc/transform/FirstTransform.scala
+++ b/src/dotty/tools/dotc/transform/FirstTransform.scala
@@ -141,12 +141,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
val tparams = tycon.tpe.typeSymbol.typeParams
val bounds = tparams.map(tparam =>
tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds)
- def instantiateUpperBound(tp: Type, argTypes: List[Type]): Type = {
- tp.substDealias(tparams, argTypes).bounds.hi
- // not that argTypes can contain a TypeBounds type for arguments that are
- // not fully determined. In that case we need to check against the hi bound.
- }
- Checking.checkBounds(args, bounds, instantiateUpperBound)
+ Checking.checkBounds(args, bounds, _.substDealias(tparams, _))
normalizeType(tree)
case tree =>
normalizeType(tree)
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 9b2e64f35..ba770cf2c 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -593,7 +593,7 @@ trait Applications extends Compatibility { self: Typer =>
else tree
if (typedArgs.length <= pt.paramBounds.length)
typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg)
- checkBounds(typedArgs, pt, tree.pos)
+ checkBounds(typedArgs, pt)
case _ =>
}
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index fdc70e207..2ff2e9e3b 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -33,27 +33,11 @@ object Checking {
/** A general checkBounds method that can be used for TypeApply nodes as
* well as for AppliedTypeTree nodes.
*/
- def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context) = {
- val argTypes = args.tpes
- for ((arg, bounds) <- args zip boundss) {
- def notConforms(which: String, bound: Type) = {
- ctx.error(
+ def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context) =
+ for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate))
+ ctx.error(
d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}",
arg.pos)
- }
- def checkOverlapsBounds(lo: Type, hi: Type): Unit = {
- //println(i"instantiating ${bounds.hi} with $argTypes")
- //println(i" = ${instantiate(bounds.hi, argTypes)}")
- val hiBound = instantiate(bounds.hi, argTypes)
- if (!(lo <:< hiBound)) notConforms("upper", hiBound)
- if (!(bounds.lo <:< hi)) notConforms("lower", bounds.lo)
- }
- arg.tpe match {
- case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi)
- case tp => checkOverlapsBounds(tp, tp)
- }
- }
- }
/** A type map which checks that the only cycles in a type are F-bounds
* and that protects all F-bounded references by LazyRefs.
@@ -192,10 +176,9 @@ trait Checking {
/** 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
- * TODO: remove pos parameter
*/
- def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = Checking.checkBounds(
- args, poly.paramBounds, (tp, argTypes) => tp.substParams(poly, argTypes))
+ def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit =
+ Checking.checkBounds(args, poly.paramBounds, _.substParams(poly, _))
/** Check that type `tp` is stable. */
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
@@ -292,7 +275,7 @@ trait NoChecking extends Checking {
import tpd._
override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
- override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = ()
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
override def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit = ()
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp