Skip to content

Commit 2a4c4e4

Browse files
committed
checkBounds refactoring
Move core logic to TypeOps, only leave error reporting in Checking. That way, we have the option of re-using the code as a simple test elsewhere.
1 parent 4e14abe commit 2a4c4e4

File tree

4 files changed

+44
-30
lines changed

4 files changed

+44
-30
lines changed

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import config.Printers._
77
import Decorators._
88
import StdNames._
99
import util.SimpleMap
10+
import collection.mutable
11+
import ast.tpd._
1012

1113
trait TypeOps { this: Context =>
1214

@@ -307,6 +309,40 @@ trait TypeOps { this: Context =>
307309
parentRefs
308310
}
309311

312+
/** An argument bounds violation is a triple consisting of
313+
* - the argument tree
314+
* - a string "upper" or "lower" indicating which bound is violated
315+
* - the violated bound
316+
*/
317+
type BoundsViolation = (Tree, String, Type)
318+
319+
/** The list of violations where arguments are not within bounds.
320+
* @param args The arguments
321+
* @param boundss The list of type bounds
322+
* @param instantiate A function that maps a bound type and the list of argument types to a resulting type.
323+
* Needed to handle bounds that refer to other bounds.
324+
*/
325+
def boundsViolations(args: List[Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): List[BoundsViolation] = {
326+
val argTypes = args.tpes
327+
val violations = new mutable.ListBuffer[BoundsViolation]
328+
for ((arg, bounds) <- args zip boundss) {
329+
def checkOverlapsBounds(lo: Type, hi: Type): Unit = {
330+
//println(i"instantiating ${bounds.hi} with $argTypes")
331+
//println(i" = ${instantiate(bounds.hi, argTypes)}")
332+
val hiBound = instantiate(bounds.hi, argTypes.mapConserve(_.bounds.hi))
333+
// Note that argTypes can contain a TypeBounds type for arguments that are
334+
// not fully determined. In that case we need to check against the hi bound of the argument.
335+
if (!(lo <:< hiBound)) violations += ((arg, "upper", hiBound))
336+
if (!(bounds.lo <:< hi)) violations += ((arg, "lower", bounds.lo))
337+
}
338+
arg.tpe match {
339+
case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi)
340+
case tp => checkOverlapsBounds(tp, tp)
341+
}
342+
}
343+
violations.toList
344+
}
345+
310346
/** Is `feature` enabled in class `owner`?
311347
* This is the case if one of the following two alternatives holds:
312348
*

src/dotty/tools/dotc/transform/FirstTransform.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer {
128128
val tparams = tycon.tpe.typeSymbol.typeParams
129129
val bounds = tparams.map(tparam =>
130130
tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds)
131-
def instantiateUpperBound(tp: Type, argTypes: List[Type]): Type = {
132-
tp.substDealias(tparams, argTypes).bounds.hi
133-
// not that argTypes can contain a TypeBounds type for arguments that are
134-
// not fully determined. In that case we need to check against the hi bound.
135-
}
136-
Checking.checkBounds(args, bounds, instantiateUpperBound)
131+
Checking.checkBounds(args, bounds, _.substDealias(tparams, _))
137132
normalizeType(tree)
138133
case tree =>
139134
normalizeType(tree)

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ trait Applications extends Compatibility { self: Typer =>
585585
else tree
586586
if (typedArgs.length <= pt.paramBounds.length)
587587
typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg)
588-
checkBounds(typedArgs, pt, tree.pos)
588+
checkBounds(typedArgs, pt)
589589
case _ =>
590590
}
591591
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,11 @@ object Checking {
3333
/** A general checkBounds method that can be used for TypeApply nodes as
3434
* well as for AppliedTypeTree nodes.
3535
*/
36-
def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context) = {
37-
val argTypes = args.tpes
38-
for ((arg, bounds) <- args zip boundss) {
39-
def notConforms(which: String, bound: Type) = {
40-
ctx.error(
36+
def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context) =
37+
for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate))
38+
ctx.error(
4139
d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}",
4240
arg.pos)
43-
}
44-
def checkOverlapsBounds(lo: Type, hi: Type): Unit = {
45-
//println(i"instantiating ${bounds.hi} with $argTypes")
46-
//println(i" = ${instantiate(bounds.hi, argTypes)}")
47-
val hiBound = instantiate(bounds.hi, argTypes)
48-
if (!(lo <:< hiBound)) notConforms("upper", hiBound)
49-
if (!(bounds.lo <:< hi)) notConforms("lower", bounds.lo)
50-
}
51-
arg.tpe match {
52-
case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi)
53-
case tp => checkOverlapsBounds(tp, tp)
54-
}
55-
}
56-
}
5741

5842
/** A type map which checks that the only cycles in a type are F-bounds
5943
* and that protects all F-bounded references by LazyRefs.
@@ -190,10 +174,9 @@ trait Checking {
190174
/** Check that type arguments `args` conform to corresponding bounds in `poly`
191175
* Note: This does not check the bounds of AppliedTypeTrees. These
192176
* are handled by method checkBounds in FirstTransform
193-
* TODO: remove pos parameter
194177
*/
195-
def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = Checking.checkBounds(
196-
args, poly.paramBounds, (tp, argTypes) => tp.substParams(poly, argTypes))
178+
def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit =
179+
Checking.checkBounds(args, poly.paramBounds, _.substParams(poly, _))
197180

198181
/** Check that type `tp` is stable. */
199182
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
@@ -290,7 +273,7 @@ trait NoChecking extends Checking {
290273
import tpd._
291274
override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
292275
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
293-
override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = ()
276+
override def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = ()
294277
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
295278
override def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit = ()
296279
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp

0 commit comments

Comments
 (0)